web-dev-qa-db-ja.com

ファイルをアップロードするためのfileReader.readAsBinaryString

FileReader.readAsBinaryStringを使用して、AJAX経由でPNGファイルをサーバーにアップロードしようとして、コードを削除しました(fileObjectはファイルに関する情報を含むオブジェクトです)。

var fileReader = new FileReader();

fileReader.onload = function(e) {
    var xmlHttpRequest = new XMLHttpRequest();
    //Some AJAX-y stuff - callbacks, handlers etc.
    xmlHttpRequest.open("POST", '/pushfile', true);
    var dashes = '--';
    var boundary = 'aperturephotoupload';
    var crlf = "\r\n";

    //Post with the correct MIME type (If the OS can identify one)
    if ( fileObject.type == '' ){
        filetype = 'application/octet-stream';
    } else {
        filetype = fileObject.type;
    }

    //Build a HTTP request to post the file
    var data = dashes + boundary + crlf + "Content-Disposition: form-data;" + "name=\"file\";" + "filename=\"" + unescape(encodeURIComponent(fileObject.name)) + "\"" + crlf + "Content-Type: " + filetype + crlf + crlf + e.target.result + crlf + dashes + boundary + dashes;

    xmlHttpRequest.setRequestHeader("Content-Type", "multipart/form-data;boundary=" + boundary);

    //Send the binary data
    xmlHttpRequest.send(data);
}

fileReader.readAsBinaryString(fileObject);

(VIを使用して)アップロードする前にファイルの最初の数行を調べると、

enter image description here

アップロードショー後の同じファイル

enter image description here

どこかでフォーマット/エンコードの問題のように見えるので、生のバイナリデータで簡単なUTF8エンコード機能を使用してみました

    function utf8encode(string) {
        string = string.replace(/\r\n/g,"\n");
        var utftext = "";

        for (var n = 0; n < string.length; n++) {

            var c = string.charCodeAt(n);

            if (c < 128) {
                utftext += String.fromCharCode(c);
            }
            else if((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }

        }

        return utftext;
    )

その後、元のコードで

//Build a HTTP request to post the file
var data = dashes + boundary + crlf + "Content-Disposition: form-data;" + "name=\"file\";" + "filename=\"" + unescape(encodeURIComponent(file.file.name)) + "\"" + crlf + "Content-Type: " + filetype + crlf + crlf + utf8encode(e.target.result) + crlf + dashes + boundary + dashes;

の出力が得られます

enter image description here

それでも未加工ファイルが何であったか=(

エンコードの問題を回避するためにファイルをエンコード/ロード/処理する方法を教えてください。HTTPリクエストで受信されるファイルは、アップロード前のファイルと同じです。

FileReader.readAsBinaryString()を使用する代わりにfileObject.getAsBinary()を使用してバイナリデータを取得する場合は、他の有用な情報があれば正常に機能します。ただし、getAsBinaryはFirefoxでのみ機能します。私はこれをMac上のFirefoxとChromeでテストしており、両方で同じ結果を得ています。バックエンドのアップロードは NGINX Upload Module によって処理され、再びMacで実行されます。サーバーとクライアントは同じマシン上にあります。アップロードしようとするファイルでも同じことが起こります。最も明白な例であるため、PNGを選択しました。

75
Smudge

fileReader.readAsDataURL( fileObject )を使用すると、base64にエンコードされ、サーバーに安全にアップロードできます。

70
c69

(以下は遅いが完全な答えです)

FileReaderメソッドのサポート


FileReader.readAsBinaryString()は廃止されました。使用しないでください!W3C File API working draft

void abort();
void readAsArrayBuffer(Blob blob);
void readAsText(Blob blob, optional DOMString encoding);
void readAsDataURL(Blob blob);

注意:Fileは一種の拡張Blob構造であることに注意してください。

MozillaはまだreadAsBinaryString()を実装し、それを MDN FileApiドキュメント で説明しています:

void abort();
void readAsArrayBuffer(in Blob blob); Requires Gecko 7.0
void readAsBinaryString(in Blob blob);
void readAsDataURL(in Blob file);
void readAsText(in Blob blob, [optional] in DOMString encoding);

readAsBinaryString()の廃止の背後にある理由は、私の意見では次のとおりです。JavaScript文字列の標準はDOMStringであり、ランダムなバイナリデータではなくUTF-8文字のみを受け入れます。したがって、readAsBinaryString()は使用しないでください。これは安全ではなく、ECMAScriptに完全に準拠していません。

JavaScript文字列はバイナリデータを保存することを想定していませんが、何らかのMozillaは可能です。それは私の意見では危険です。 Blobおよびtyped arraysArrayBufferおよび未実装のStringView)は、UTFなしで純粋なバイナリデータの使用を許可する目的で発明されました。 8文字列の制限。

XMLHttpRequestアップロードのサポート


XMLHttpRequest.send() には、次の呼び出しオプションがあります。

void send();
void send(ArrayBuffer data);
void send(Blob data);
void send(Document data);
void send(DOMString? data);
void send(FormData data);

XMLHttpRequest.sendAsBinary() には、次の呼び出しオプションがあります。

void sendAsBinary(   in DOMString body );

sendAsBinary()は標準ではないため、Chromeではサポートされない場合があります。

解決策


したがって、いくつかのオプションがあります。

  1. send()FileReader.readAsArrayBuffer ( fileObject )FileReader.result。操作するのはより複雑ですが(個別にsend()を作成する必要があります)、それはRECOMMENDED APPROACHです。
  2. send()FileReader.readAsDataURL( fileObject )FileReader.result。無駄なオーバーヘッドと圧縮レイテンシを生成し、サーバー側での解凍手順が必要ですが、JavaScriptの文字列として簡単に操作できます。
  3. 非標準であり、sendAsBinary()FileReader.readAsBinaryString( fileObject )FileReader.resultである

MDN は次のように述べています:

(ファイルのアップロードのように)バイナリコンテンツを送信する最良の方法は、send()メソッドと組み合わせてArrayBuffersまたはBlobを使用することです。ただし、文字列化可能な生データを送信する場合は、代わりにsendAsBinary()メソッドを使用するか、StringView(非ネイティブ)型配列スーパークラスを使用します。

99
KrisWebDev

それをサポートするブラウザーでの最良の方法は、ファイルをBlobとして送信するか、マルチパートフォームが必要な場合はFormDataを使用することです。そのためにFileReaderは必要ありません。これは、データを読み取ろうとするよりも簡単で効率的です。

multipart/form-dataとして送信する場合は、FormDataオブジェクトを使用できます。

var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.open("POST", '/pushfile', true);
var formData = new FormData();
// This should automatically set the file name and type.
formData.append("file", file);
// Sending FormData automatically sets the Content-Type header to multipart/form-data
xmlHttpRequest.send(formData);

multipart/form-dataを使用する代わりに、データを直接送信することもできます。 ドキュメント を参照してください。もちろん、これにはサーバー側の変更も必要です。

// file is an instance of File, e.g. from a file input.
var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.open("POST", '/pushfile', true);

xmlHttpRequest.setRequestHeader("Content-Type", file.type);

// Send the binary data.
// Since a File is a Blob, we can send it directly.
xmlHttpRequest.send(file);

ブラウザのサポートについては、 http://caniuse.com/#feat=xhr2 (IE 10+を含むほとんどのブラウザ)を参照してください。

20
Ralf