86

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 を使用して) ファイルの最初の数行を調べると、

ここに画像の説明を入力

アップロード後の同じファイルが表示されます

ここに画像の説明を入力

どこかのフォーマット/エンコーディングの問題のようです。生のバイナリデータで単純な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;

の出力が得られます

ここに画像の説明を入力

生のファイルが何であったかはまだわかりません =(

エンコードの問題を回避するためにファイルをエンコード/ロード/処理するにはどうすればよいですか。HTTP リクエストで受信されるファイルは、アップロード前のファイルと同じになります。

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

4

3 に答える 3

111

(以下は遅れていますが完全な回答です)

FileReader メソッドのサポート


FileReader.readAsBinaryString()廃止されました使わないで!W3C File API ワーキング ドラフトには含まれていません。

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

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

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

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 arrays(ArrayBufferおよびまだ実装されていないが必須ではないStringView) は、UTF-8 文字列の制限なしで、純粋なバイナリ データを使用できるようにするという 1 つの目的のために考案されました。

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.result_ FileReader.readAsArrayBuffer ( fileObject )操作はより複雑です (別の send() を作成する必要があります) が、それはRECOMMENDED APPROACHです。
  2. send()の。FileReader.result_ FileReader.readAsDataURL( fileObject )無駄なオーバーヘッドと圧縮の待ち時間が発生し、サーバー側で解凍手順が必要になりますが、Javascript で文字列として操作するのは簡単です。
  3. 非標準であることとsendAsBinary()FileReader.resultFileReader.readAsBinaryString( fileObject )

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

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

于 2013-07-07T12:26:05.070 に答える
75

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

于 2011-09-15T13:17:25.247 に答える
26

それをサポートするブラウザーでの最善の方法は、ファイルを 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 以降を含むほとんどのブラウザー) を参照してください。

于 2014-10-20T09:50:18.637 に答える