Пытаясь использовать 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, снова работающего на Mac. Сервер и клиент находятся на одной машине. То же самое происходит с любым файлом, который я пытаюсь загрузить, я просто выбрал PNG, потому что это был самый очевидный пример.
Ответ 1
Используйте fileReader.readAsDataURL( fileObject )
, это закодирует его на base64, который вы можете безопасно загрузить на свой сервер.
Ответ 2
(Ниже приведен неполный, но полный ответ)
Поддерживаются методы 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()
deveration является, на мой взгляд, следующее: стандарт для строк 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.
Решение
Итак, у вас есть несколько вариантов:
-
send()
FileReader.result
of FileReader.readAsArrayBuffer ( fileObject )
. Сложнее манипулировать (вам нужно сделать для него отдельный send()), но это РЕКОМЕНДУЕМЫЙ ПОДХОД.
-
send()
FileReader.result
of FileReader.readAsDataURL( fileObject )
. Он генерирует бесполезную задержку накладных расходов и сжатие, для этого требуется этап декомпрессии на стороне сервера, но его легко манипулировать как строку в Javascript.
- Будучи нестандартным и
sendAsBinary()
FileReader.result
of FileReader.readAsBinaryString( fileObject )
MDN утверждает, что:
Лучший способ отправки двоичного содержимого (например, при загрузке файлов) используется ArrayBuffers или Blobs в соединении с методом send(). Однако, если вы хотите отправить стробируемые необработанные данные, используйте sendAsBinary() вместо этого, или суперкласс класса типизированных массивов StringView (Non native).
Ответ 3
Лучший способ в браузерах, которые его поддерживают, - отправить файл как 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 +).