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


83

fileReader.readAsBinaryStringを使用してPNGファイルをAJAX経由でサーバーにアップロードしようとすると、コードが削除されます(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でテストしてきましたが、どちらでも同じ結果が得られました。バックエンドのアップロードはNGINXアップロードモジュールによって処理されており、これもMacで実行されています。サーバーとクライアントは同じマシン上にあります。アップロードしようとするファイルでも同じことが起こります。最も明白な例であるため、PNGを選択しました。

回答:


74

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


8
それが機能している間、サーバーに保存されているファイルのバージョンはBase64でエンコードされています(そうあるべきです)。Base64エンコードではなくバイナリデータとして転送する方法はありませんか(<input type="file">
つまり

2
サーバーにPHPがある場合は、保存する前にbase64_decode(file)を実行できます。そして、いいえ-httpを介して生のバイナリデータを転送する安全な方法はありません。
c69 2011

readAsDataURLを使用すると、サーバー上でこのimgur.com/1LHyaが得られ、PHPのbase64_decodeを介して実行されます(実際にはPythonを使用していますが、PHPは良いテストです)imgur.com/0uwhyを取得しますが、まだ元のバイナリデータではありません。有効な画像ではありません=(
2011

20
@ imgur.com/1LHya O、私の悪い!サーバーでは、base64文字列を「、」で分割し、2番目の部分のみを保存する必要があります。これにより、mimeタイプは実際のファイルコンテンツとともに保存されません。
c69 2011

7
いいえ、効率的ではありません。これにより、ファイルサイズが137%増加し、サーバーのオーバーヘッドが発生します。しかし、F *** IEをサポートする他の方法はありません
puchu 2013

109

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

FileReaderメソッドのサポート


FileReader.readAsBinaryString()非推奨です。使用しないでください!W3C File APIワーキングドラフトには含まれていません:

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

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

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

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文字列の制限なしに純粋なバイナリデータの使用を許可するという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.resultFileReader.readAsArrayBuffer ( fileObject )。操作はより複雑ですが(別のsend()を作成する必要があります)、これが推奨されるアプローチです。
  2. send()FileReader.resultFileReader.readAsDataURL( fileObject )。無駄なオーバーヘッドと圧縮待ち時間が発生し、サーバー側で解凍手順が必要になりますが、Javascriptで文字列として操作するのは簡単です。
  3. 非標準でsendAsBinary()ありFileReader.resultFileReader.readAsBinaryString( fileObject )

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

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


10
私は、再びこれを掘ることだけを介して行われたバイナリデータ(などA PDFファイル)を送信することはおそらく最も簡単な方法を追加したいごめんなさいFileReader.readAsDataURLと上onloadハンドラの代わりに、単に送信event.target.result(クリーンbase64でエンコードされた文字列ではありません)します最初にのような正規表現でクリーンアップしevent.target.result = event.target.result.match(/,(.*)$/)[1]、実際のbase64をサーバーに送信してデコードします。

MDNは誰でも編集できるので、ソースとしては使用しないでしょう。
クリスアンダーソン

4
@ user1299518、より良い使用event.target.result.split(",", 2)[1]、ではなくmatch
MrKsn 2016

1
@KrisWebDev:推奨オプションで、別のsend()を作成する必要があると述べています。どうして?
Readren 2017

推奨されるアプローチは、TFS RESTAPIを使用して添付ファイルをアップロードするために機能しました。どうも!
RoJaIt

24

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


xmlHttpRequest.send(formData);
Li-chih Wu 2014

7
最後に、を使用せずに適切な答えFormData。誰もがフォームを使用しているようですが、必要なのは1つのファイルをアップロードすることだけです...ありがとう!
ウィルト

私はajaxを介してmp3ファイルをアップロードするためにこれを機能させる方法を何時間も探していました、これはトリックを行います!
ジャスティンヴィンセント

setRequestHeaderは、formDataを送信することで自動設定され、「Content-Type:multipart / form-data; border = ---- WebKitFormBoundaryQA8d7glpaso6zKsA」のようになります。私の場合、壊れたと思います。 setRequestHeaderを削除しない限り、CORS。
ジャスティンヴィンセント

注:上記の私のコメントは、formDataオブジェクトを使用する場合にのみ適用されます。
ジャスティンヴィンセント
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.