Tratando de usar fileReader.readAsBinaryString para cargar un archivo PNG al servidor a través de AJAX, código reducido (fileObject es el objeto que contiene información sobre mi archivo);
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);
Examinar las primeras líneas de un archivo antes de cargarlo (usando VI) me da
El mismo archivo después de la carga muestra
Entonces parece un problema de formato / codificación en algún lugar, intenté usar una función de codificación UTF8 simple en los datos binarios sin procesar
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;
)
Luego en el código original
//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;
lo que me da la salida de
Todavía no es lo que era el archivo sin procesar = (
¿Cómo codifico / cargo / proceso el archivo para evitar los problemas de codificación, por lo que el archivo que se recibe en la solicitud HTTP es el mismo que el archivo antes de que se cargara?
Alguna otra información posiblemente útil, si en lugar de usar fileReader.readAsBinaryString () utilizo fileObject.getAsBinary () para obtener los datos binarios, funciona bien. Pero getAsBinary solo funciona en Firefox. He estado probando esto en Firefox y Chrome, ambos en Mac, obteniendo el mismo resultado en ambos. Las cargas de backend las gestiona el módulo de carga de NGINX , que se ejecuta nuevamente en Mac. El servidor y el cliente están en la misma máquina. Lo mismo sucede con cualquier archivo que intento cargar, simplemente elegí PNG porque era el ejemplo más obvio.
fuente
<input type="file">
campo normal )?(Lo que sigue es una respuesta tardía pero completa)
Soporte de métodos FileReader
FileReader.readAsBinaryString()
está en desuso. ¡No lo uses! Ya no está en el borrador de trabajo de la API de archivos W3C :void abort(); void readAsArrayBuffer(Blob blob); void readAsText(Blob blob, optional DOMString encoding); void readAsDataURL(Blob blob);
NB: Tenga en cuenta que
File
es una especie deBlob
estructura extendida .Mozilla todavía lo implementa
readAsBinaryString()
y lo describe en la documentación de 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);
En
readAsBinaryString()
mi opinión, la razón detrás de la desaprobación es la siguiente: el estándar para las cadenas de JavaScript son lasDOMString
que solo aceptan caracteres UTF-8, NO datos binarios aleatorios. Así que no use readAsBinaryString (), eso no es seguro ni cumple con ECMAScript en absoluto.Sabemos que no se supone que las cadenas de JavaScript almacenen datos binarios, pero Mozilla de alguna manera sí puede. Eso es peligroso en mi opinión.
Blob
ytyped arrays
(ArrayBuffer
y los que aún no se han implementado pero no necesariosStringView
) se inventaron con un propósito: permitir el uso de datos binarios puros, sin restricciones de cadenas UTF-8.Soporte de carga XMLHttpRequest
XMLHttpRequest.send()
tiene las siguientes opciones de invocación:void send(); void send(ArrayBuffer data); void send(Blob data); void send(Document data); void send(DOMString? data); void send(FormData data);
XMLHttpRequest.sendAsBinary()
tiene las siguientes opciones de invocación:void sendAsBinary( in DOMString body );
sendAsBinary () NO es un estándar y puede que no sea compatible con Chrome.
Soluciones
Entonces tienes varias opciones:
send()
elFileReader.result
deFileReader.readAsArrayBuffer ( fileObject )
. Es más complicado de manipular (tendrá que hacer un send () por separado) pero es el ENFOQUE RECOMENDADO .send()
elFileReader.result
deFileReader.readAsDataURL( fileObject )
. Genera sobrecarga inútil y latencia de compresión, requiere un paso de descompresión en el lado del servidor, PERO es fácil de manipular como una cadena en Javascript.sendAsBinary()
elFileReader.result
deFileReader.readAsBinaryString( fileObject )
MDN afirma que:
fuente
FileReader.readAsDataURL
delonload
controlador en lugar de simplemente enviarevent.target.result
(que no es una cadena codificada en base64 limpia) límpielo primero con un poco de expresión regularevent.target.result = event.target.result.match(/,(.*)$/)[1]
y envíe la base64 real al servidor para decodificarla.event.target.result.split(",", 2)[1]
, nomatch
.La mejor manera en los navegadores que lo admiten es enviar el archivo como un Blob o usar FormData si desea un formulario de varias partes. No necesita un FileReader para eso. Esto es más simple y más eficiente que intentar leer los datos.
Si desea enviarlo específicamente como
multipart/form-data
, puede usar un objeto 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);
También puede enviar los datos directamente, en lugar de usar
multipart/form-data
. Consulte la documentación . Por supuesto, esto también necesitará un cambio del lado del servidor.// 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);
Para obtener compatibilidad con el navegador, consulte: http://caniuse.com/#feat=xhr2 (la mayoría de los navegadores, incluido IE 10+).
fuente
FormData
. Parece que todos están usando un formulario, mientras que todo lo que necesitan es cargar un solo archivo ... ¡Gracias!