79 votes

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 les informations sur mon fichier);

var fileReader = new FileReader();

fileReader.onload = function(e) {
    var xmlHttpRequest = new XMLHttpRequest();
    //Quelques trucs AJAX - callbacks, gestionnaires, etc.
    xmlHttpRequest.open("POST", '/pushfile', true);
    var dashes = '--';
    var boundary = 'aperturephotoupload';
    var crlf = "\r\n";

    //Postez avec le bon type MIME (si le système d'exploitation peut en identifier un)
    if ( fileObject.type == '' ){
        filetype = 'application/octet-stream';
    } else {
        filetype = fileObject.type;
    }

    //Construire une requête HTTP pour envoyer le fichier
    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);

    //Envoyer les données binaires
    xmlHttpRequest.send(data);
}

fileReader.readAsBinaryString(fileObject);

Examiner les premières lignes d'un fichier avant de le télécharger (en utilisant VI) me donne

enter image description here

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

enter image description here

Il semble donc y avoir un problème de formatage/codage quelque part, j'ai essayé d'utiliser une simple fonction d'encodage 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;
    )

Ensuite, dans le code d'origine

//Construire une requête HTTP pour envoyer le fichier
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 qu'était le fichier brut =(

Comment encodage/charger/traiter le fichier pour éviter les problèmes d'encodage, afin que le fichier reçu dans la requête HTTP soit le même que le fichier avant d'être téléchargé.

Quelques autres informations potentiellement 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, les deux sur Mac, obtenant le même résultat dans les deux cas. Les téléchargements côté serveur sont gérés par le Module de téléchargement NGINX, également en cours d'exécution sur Mac. Le serveur et le client sont sur la même machine. La même chose se produit avec n'importe quel fichier que j'essaie de télécharger, j'ai juste choisi PNG car c'était l'exemple le plus évident.

102voto

KrisWebDev Points 1788

(Voici une réponse tardive mais complète)

Prise en charge des méthodes FileReader


FileReader.readAsBinaryString() est obsolète. Ne l'utilisez pas! Il n'apparaît plus dans le brouillon de travail de l'API de fichiers du W3C:

void abort();
void readAsArrayBuffer(Blob blob);
void readAsText(Blob blob, encodage DOMString facultatif);
void readAsDataURL(Blob blob);

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

Firefox implémente toujours readAsBinaryString() et le décrit dans la documentation MDN FileApi:

void abort();
void readAsArrayBuffer(in Blob blob); Nécessite Gecko 7.0
void readAsBinaryString(in Blob blob);
void readAsDataURL(in Blob fichier);
void readAsText(in Blob blob, [facultatif] encodage DOMString);

La raison de l'obsolescence de readAsBinaryString() est, à mon avis, la suivante: la norme pour les chaînes de caractères JavaScript est DOMString qui n'accepte que des caractères UTF-8, PAS des données binaires aléatoires. Donc ne pas utiliser readAsBinaryString(), ce n'est pas sûr et ne respecte pas du tout les normes ECMAScript.

Nous savons que les chaînes de caractères JavaScript ne sont pas censées stocker des données binaires mais Mozilla peut le faire d'une certaine manière. C'est dangereux à mon avis. Blob et les tableaux typés (ArrayBuffer et le non implémenté mais non nécessaire StringView) ont été inventés dans un seul but: permettre l'utilisation de données binaires pures, sans restrictions liées aux chaînes UTF-8.

Prise en charge de l'envoi de XMLHttpRequest


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

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'invocation suivantes:

void sendAsBinary(   in DOMString body );

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

Solutions


Vous avez donc plusieurs options:

  1. send() le FileReader.result de FileReader.readAsArrayBuffer ( fileObject ). C'est plus compliqué à manipuler (vous devrez faire un envoi séparé) mais c'est l'APPROCHE RECOMMANDÉE.
  2. send() le FileReader.result de FileReader.readAsDataURL( fileObject ). Cela génère un surdébit inutile et une latence de compression, nécessite une étape de décompression côté serveur MAIS c'est facile à manipuler comme une chaîne de caractères en JavaScript.
  3. Étant non standard et sendAsBinary() le FileReader.result de FileReader.readAsBinaryString( fileObject )

MDN déclare que:

La meilleure façon d'envoyer du contenu binaire (comme dans les téléchargements de fichiers) est d'utiliser les ArrayBuffers ou Blobs en conjonction avec la méthode send(). Cependant, si vous voulez envoyer des données brutes qui peuvent être transformées en chaînes, utilisez plutôt la méthode sendAsBinary() ou la superclasse des tableaux typés StringView (Non natifs).

72voto

c69 Points 5140

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

23voto

Ralf Points 3732

La meilleure façon dans les navigateurs qui le supportent est d'envoyer le fichier sous forme de Blob, ou en utilisant FormData si vous voulez un formulaire multipart. 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();
// Cela devrait définir automatiquement le nom et le type du fichier.
formData.append("file", file);
// L'envoi de FormData définit automatiquement l'en-tête Content-Type sur multipart/form-data
xmlHttpRequest.send(formData);

Vous pouvez également envoyer les données directement, au lieu d'utiliser multipart/form-data. Voir la [documentation](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#send()). Bien sûr, cela nécessitera également un changement côté serveur.

// file est une instance de File, par ex. d'un input file.
var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.open("POST", '/pushfile', true);

xmlHttpRequest.setRequestHeader("Content-Type", file.type);

// Envoyer les données binaires.
// Comme un File est un Blob, nous pouvons l'envoyer directement.
xmlHttpRequest.send(file);

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X