web-dev-qa-db-fra.com

fileReader.readAsBinaryString pour télécharger des fichiers

Essayer d'utiliser fileReader.readAsBinaryString pour télécharger un fichier PNG sur le serveur via AJAX, code simplifié (fileObject est l'objet contenant des informations sur mon fichier);

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);

L’examen des premières lignes d’un fichier avant le téléchargement (avec VI) me donne

enter image description here

Le même fichier après le téléchargement montre

enter image description here

Donc, cela ressemble à un problème de formatage/encodage quelque part, j'ai essayé d'utiliser une simple fonction de codage UTF8 sur les données binaires brutes

    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;
    )

Puis dans le code d'origine

//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;

ce qui me donne la sortie de

enter image description here

Toujours pas ce que le fichier brut était = (

Comment puis-je encoder/charger/traiter le fichier pour éviter les problèmes d’encodage, de sorte que le fichier reçu dans la demande HTTP soit le même que le fichier avant son téléchargement.

Quelques autres informations éventuellement utiles, si au lieu d'utiliser fileReader.readAsBinaryString (), j'utilise fileObject.getAsBinary () pour obtenir les données binaires, cela fonctionne bien. Mais getAsBinary ne fonctionne que dans Firefox. J'ai testé cela dans Firefox et Chrome, tous deux sur Mac, obtenant le même résultat dans les deux cas. Les téléchargements dorsaux sont gérés par le module de téléchargement de NGINX , exécuté à nouveau sur Mac. Le serveur et le client sont sur le même ordinateur. La même chose se produit avec tous les fichiers que j'essaie de télécharger, j'ai simplement choisi PNG parce que c'était l'exemple le plus évident.

75
Smudge

Utilisez fileReader.readAsDataURL( fileObject ), cela l'encodera en base64, que vous pourrez télécharger en toute sécurité sur votre serveur.

70
c69

(La réponse est tardive mais complète.)

Prise en charge des méthodes FileReader


FileReader.readAsBinaryString() est obsolète. Ne l'utilisez pas! Ce n'est plus dans le brouillon brouillon de travail de l'API de fichier W3C :

void abort();
void readAsArrayBuffer(Blob blob);
void readAsText(Blob blob, optional DOMString encoding);
void readAsDataURL(Blob blob);

NB: Notez que File est une sorte de structure étendue Blob.

Mozilla implémente toujours readAsBinaryString() et le décrit dans documentation 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 raison de la désapprobation de readAsBinaryString() est, à mon avis, la suivante: la norme pour les chaînes JavaScript est DOMString qui n'accepte que les caractères UTF-8, PAS les données binaires aléatoires. Donc, n'utilisez pas readAsBinaryString (), ce n'est pas du tout sûr et conforme à ECMAScript.

Nous savons que les chaînes JavaScript ne sont pas supposées stocker des données binaires , mais que Mozilla peut le faire. C'est dangereux à mon avis. Blob et typed arrays (ArrayBuffer et le StringView pas encore implémenté mais pas nécessaire) ont été inventés dans un seul but: permettre l'utilisation de données binaires pures , sans restrictions de chaînes UTF-8.

Prise en charge du téléchargement XMLHttpRequest


XMLHttpRequest.send() a les options d'appel suivantes:

void send();
void send(ArrayBuffer data);
void send(Blob data);
void send(Document data);
void send(DOMString? data);
void send(FormData data);

XMLHttpRequest.sendAsBinary() a les options d'appel suivantes:

void sendAsBinary(   in DOMString body );

sendAsBinary () n'est PAS un standard et peut ne pas être pris en charge dans Chrome.

Solutions


Donc, vous avez plusieurs options:

  1. send() le FileReader.result de FileReader.readAsArrayBuffer ( fileObject ). C’est plus compliqué à manipuler (vous devrez faire un send () séparé), mais c’est l’APPROCHE RECOMMANDÉE .
  2. send() le FileReader.result de FileReader.readAsDataURL( fileObject ). Il génère une surcharge inutile et une latence de compression, nécessite une étape de décompression côté serveur MAIS il est facile à manipuler sous forme de chaîne en Javascript.
  3. Étant non standard et sendAsBinary() le FileReader.result De FileReader.readAsBinaryString( fileObject )

MDN déclare que:

Le meilleur moyen d'envoyer du contenu binaire (comme dans le téléchargement de fichiers) consiste à utiliser ArrayBuffers ou Blobs en conjonction avec la méthode send (). Toutefois, si vous souhaitez envoyer des données brutes pouvant faire l'objet d'une chaîne, utilisez plutôt la méthode sendAsBinary () ou la superclasse de tableaux typés StringView (non natif).

99
KrisWebDev

Dans les navigateurs qui le prennent en charge, le meilleur moyen consiste à envoyer le fichier sous forme de blob ou à l'aide de FormData si vous souhaitez un formulaire en plusieurs parties. Vous n'avez pas besoin d'un FileReader pour cela. C'est à la fois plus simple et plus efficace que d'essayer de lire les données.

Si vous voulez spécifiquement l'envoyer en tant que multipart/form-data, vous pouvez utiliser un objet 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);

Vous pouvez également envoyer les données directement au lieu d'utiliser multipart/form-data. Voir le documentation . Bien entendu, cela nécessitera également une modification côté serveur.

// 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);

Pour le support des navigateurs, voir: http://caniuse.com/#feat=xhr2 (la plupart des navigateurs, y compris IE 10+).

20
Ralf