FileReader.readAsBinaryString para subir archivos


Intentando usar FileReader.readAsBinaryString para subir un archivo PNG al servidor a través de AJAX, código despojado (FileObject es el objeto que contiene información en 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

introduzca la descripción de la imagen aquí

El mismo archivo después de cargar muestra

introduzca la descripción de la imagen aquí

Por lo que parece un problema de formato / codificación en algún lugar, intenté usar una función de codificación UTF8 simple en el binario raw datos

    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;

Que me da la salida de

introduzca la descripción de la imagen aquí

Todavía no lo que el archivo raw era =(

Cómo puedo codificar/cargar/procesar 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 () Uso FileObject.getAsBinary() para obtener los datos binarios, se 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 subidas de backend están siendo manejadas por el Módulo de carga NGINX , nuevamente ejecutándose en Mac. El servidor y el cliente están en la misma máquina. Lo mismo sucede con cualquier archivo que intento cargar, solo elegí PNG porque era el ejemplo más obvio.

Author: Smudge, 2011-09-15

3 answers

Use fileReader.readAsDataURL( fileObject ), esto lo codificará en base64, que puede cargar de forma segura en su servidor.

 65
Author: c69,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-15 13:17:25

(Lo que sigue es una respuesta tardía pero completa)

Soporte de métodos FileReader


FileReader.readAsBinaryString() es obsoleto. No lo use! Ya no está en el W3C File API working draft :

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 de estructura extendida Blob.

Mozilla todavía 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);

La razón detrás de readAsBinaryString() la desaprobación es, en mi opinión, la siguiente: las cadenas estándar para JavaScript son DOMString que solo aceptan caracteres UTF-8, NO datos binarios aleatorios. Así que no uses readAsBinaryString (), eso no es seguro y cumple con ECMAScript en absoluto.

Sabemos que Las cadenas JavaScript no se supone que almacenen datos binarios pero Mozilla en algún tipo puede. Eso es peligroso en mi opinión. Blob y typed arrays (ArrayBuffer y los aún no implementados pero no necesarios StringView) se inventaron con un propósito: permitir el uso de datos binarios puros, sin Restricciones de cadenas UTF-8.

XMLHttpRequest soporte de carga


XMLHttpRequest.send() tiene las siguientes opciones de invocaciones:

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 invocaciones:

void sendAsBinary(   in DOMString body );

SendAsBinary() NO es un estándar y puede no ser soportado en Chrome.

Soluciones


Así que tienes varias opciones:

  1. send() el FileReader.result de FileReader.readAsArrayBuffer ( fileObject ). Es más complicado de manipular (tendrás que hacer un separe send () para ello) pero es el ENFOQUE RECOMENDADO.
  2. send() el FileReader.result de FileReader.readAsDataURL( fileObject ). Genera una 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.
  3. Siendo no-estándar y sendAsBinary() el FileReader.result de FileReader.readAsBinaryString( fileObject )

MDN afirma que:

La mejor manera de enviar contenido binario (como en la carga de archivos) es usando ArrayBuffers o Blobs en conjunción con el método send (). Obstante, si desea enviar datos raw stringifiable, utilice sendAsBinary() método en su lugar, o la superclase de matrices de tipo StringView (No nativa).

 91
Author: KrisWebDev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-22 18:14:14

La mejor manera en los navegadores que lo soportan, es enviar el archivo como un Blob, o usando FormData si desea un formulario multiparte. No necesitas 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. Véase la documentación . Por supuesto, esto necesitará un cambio del lado del servidor como bien.

// 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 el soporte del navegador, ver: http://caniuse.com/#feat=xhr2 (la mayoría de los navegadores, incluyendo IE 10+).

 15
Author: Ralf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-25 13:35:08