Próba użycia fileReader.readAsBinaryString do przesłania pliku PNG na serwer przez AJAX, uproszczony kod (fileObject to obiekt zawierający informacje o moim pliku);
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);
Zbadanie kilku pierwszych wierszy pliku przed załadowaniem (używając VI) daje mi wyniki
Ten sam plik po przesłaniu pokazuje
Więc wygląda na to, że gdzieś występuje problem z formatowaniem / kodowaniem, próbowałem użyć prostej funkcji kodowania UTF8 na surowych danych binarnych
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;
)
Następnie w oryginalnym kodzie
//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;
co daje mi wynik
Nadal nie jest to, czym był surowy plik = (
Jak zakodować / załadować / przetworzyć plik, aby uniknąć problemów z kodowaniem, aby plik odbierany w żądaniu HTTP był taki sam, jak plik przed przesłaniem.
Kilka innych potencjalnie przydatnych informacji, jeśli zamiast używać fileReader.readAsBinaryString () używam fileObject.getAsBinary () do pobierania danych binarnych, działa dobrze. Ale getAsBinary działa tylko w przeglądarce Firefox. Testowałem to w przeglądarce Firefox i Chrome, oba na Macu, uzyskując ten sam wynik w obu. Przesyłanie do zaplecza jest obsługiwane przez moduł NGINX Upload , ponownie działający na komputerze Mac. Serwer i klient znajdują się na tym samym komputerze. To samo dzieje się z każdym plikiem, który próbuję przesłać, po prostu wybrałem PNG, ponieważ był to najbardziej oczywisty przykład.
źródło
<input type="file">
pola)(Poniżej znajduje się późna, ale pełna odpowiedź)
Obsługa metod FileReader
FileReader.readAsBinaryString()
jest przestarzały. Nie używaj tego! Nie ma go już w roboczej wersji roboczej W3C File API :void abort(); void readAsArrayBuffer(Blob blob); void readAsText(Blob blob, optional DOMString encoding); void readAsDataURL(Blob blob);
Uwaga: należy zauważyć, że
File
jest to rodzaj rozbudowanejBlob
struktury.Mozilla nadal implementuje
readAsBinaryString()
i opisuje to w dokumentacji 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()
Moim zdaniem powód wycofania jest następujący: standard dla ciągów JavaScriptDOMString
akceptuje tylko znaki UTF-8, a NIE losowe dane binarne. Więc nie używaj readAsBinaryString (), jest to w ogóle niebezpieczne i zgodne z ECMAScript.Wiemy, że łańcuchy JavaScript nie powinny przechowywać danych binarnych, ale Mozilla może to zrobić. Moim zdaniem to niebezpieczne.
Blob
ityped arrays
(ArrayBuffer
i jeszcze nie zaimplementowane, ale niekonieczneStringView
) zostały wymyślone w jednym celu: zezwolić na użycie czystych danych binarnych, bez ograniczeń dotyczących łańcuchów UTF-8.Obsługa przesyłania XMLHttpRequest
XMLHttpRequest.send()
ma następujące opcje wywołań:void send(); void send(ArrayBuffer data); void send(Blob data); void send(Document data); void send(DOMString? data); void send(FormData data);
XMLHttpRequest.sendAsBinary()
ma następujące opcje wywołań:void sendAsBinary( in DOMString body );
sendAsBinary () NIE jest standardem i może nie być obsługiwana w przeglądarce Chrome.
Rozwiązania
Masz więc kilka opcji:
send()
FileReader.result
odFileReader.readAsArrayBuffer ( fileObject )
. Manipulowanie jest bardziej skomplikowane (będziesz musiał wykonać osobną wysyłkę ()), ale jest to PODEJŚCIE ZALECANE .send()
FileReader.result
odFileReader.readAsDataURL( fileObject )
. Generuje bezużyteczne narzuty i opóźnienie kompresji, wymaga kroku dekompresji po stronie serwera, ALE jest łatwy do manipulowania jako ciąg znaków w JavaScript.sendAsBinary()
FileReader.result
FileReader.readAsBinaryString( fileObject )
MDN stwierdza, że:
źródło
FileReader.readAsDataURL
ionload
obsługi, a nie tylko wysyłaniaevent.target.result
(co nie jest czystą base64 zakodowany ciąg znaków), który wyczyść go najpierw za pomocą pewnych wyrażeń regularnychevent.target.result = event.target.result.match(/,(.*)$/)[1]
i wyślij prawdziwy base64 do serwera w celu zdekodowania.event.target.result.split(",", 2)[1]
, niematch
.Najlepszym sposobem w przeglądarkach, które go obsługują, jest wysłanie pliku jako obiektu BLOB lub użycie FormData, jeśli potrzebujesz formularza wieloczęściowego. Nie potrzebujesz do tego FileReadera. Jest to prostsze i wydajniejsze niż próba odczytania danych.
Jeśli chcesz wysłać go jako
multipart/form-data
, możesz użyć obiektu 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);
Możesz również wysłać dane bezpośrednio, zamiast używać
multipart/form-data
. Zobacz dokumentację . Oczywiście będzie to również wymagało zmiany po stronie serwera.// 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);
Informacje o obsłudze przeglądarek można znaleźć pod adresem : http://caniuse.com/#feat=xhr2 (większość przeglądarek, w tym IE 10+).
źródło
FormData
. Wygląda na to, że wszyscy używają formularza, podczas gdy jedyne, czego potrzebują, to przesłanie jednego pliku ... Dzięki!