fileReader.readAsBinaryString для загрузки файлов - PullRequest
75 голосов
/ 15 сентября 2011

Попытка использовать 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) дает мне

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.Я тестировал это в Firefox и Chrome, оба на Mac, получая одинаковый результат в обоих.Внутренние загрузки обрабатываются NGINX Upload Module , снова работающим на Mac.Сервер и клиент находятся на одной машине.То же самое происходит с любым файлом, который я пытаюсь загрузить, я просто выбрал PNG, потому что это был самый очевидный пример.

Ответы [ 3 ]

99 голосов
/ 07 июля 2013

(Ниже приводится поздний, но полный ответ)

Поддержка методов 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() и описывает это в Документация 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 arrays (ArrayBuffer и еще не реализованные, но не обязательные 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.result из FileReader.readAsArrayBuffer ( fileObject ). Сложнее манипулировать (вам придется создать для него отдельный send ()), но это РЕКОМЕНДУЕМЫЙ ПОДХОД .
  2. send() FileReader.result из FileReader.readAsDataURL( fileObject ). Он генерирует бесполезные издержки и задержку сжатия, требует этапа распаковки на стороне сервера, НО его легко манипулировать как строку в Javascript.
  3. Будучи нестандартным и sendAsBinary() FileReader.result из FileReader.readAsBinaryString( fileObject )

MDN гласит:

Лучший способ отправки двоичного содержимого (например, при загрузке файлов) - это использование ArrayBuffers или Blobs в сочетании с методом send (). Тем не мение, если вы хотите отправить необработанные данные в виде строк, используйте sendAsBinary () вместо этого, или суперкласс типизированных массивов StringView (не собственный).

69 голосов
/ 15 сентября 2011

Используйте fileReader.readAsDataURL( fileObject ), это закодирует его в base64, который вы можете безопасно загрузить на свой сервер.

20 голосов
/ 20 октября 2014

Лучший способ в браузерах, которые его поддерживают, - отправить файл в виде 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+).

...