ArrayBuffer à la chaîne encodée en base64

203

J'ai besoin d'un moyen efficace (en lecture native) pour convertir un ArrayBufferen une chaîne base64 qui doit être utilisée sur un article en plusieurs parties.

zaheer
la source

Réponses:

222
function _arrayBufferToBase64( buffer ) {
    var binary = '';
    var bytes = new Uint8Array( buffer );
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
        binary += String.fromCharCode( bytes[ i ] );
    }
    return window.btoa( binary );
}

mais les implémentations non natives sont plus rapides, par exemple https://gist.github.com/958841 voir http://jsperf.com/encoding-xhr-image-data/6

mobz
la source
10
J'ai essayé l'implémentation non native à partir du lien et il a fallu 1 minute et demie pour convertir un tampon de taille 1M tandis que le code de boucle ci-dessus n'a pris que 1 seconde.
cshu
1
J'aime la simplicité de cette approche, mais toute cette concaténation de chaînes peut être coûteuse. Il semble que la construction d'un tableau de caractères et join()leur ing à la fin est beaucoup plus rapide sur Firefox, IE et Safari (mais beaucoup plus lent sur Chrome): jsperf.com/tobase64-implementations
JLRishe
J'utilise cette fonction pour la conversion du tampon de tableau en base64 mais je ne suis pas en mesure de récupérer le tampon de tableau. J'ai écrit une fonction _base64ToArrayBuffer () ici: codeshare.io/PT4pb mais cela me donne une erreur comme:Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
bawejakunal
cela ne fonctionne pas ce JSZip. je trouve une autre façon github.com/michael/github/issues/137
RouR
1
J'essaie de télécharger un fichier pdf de 50 Mo en utilisant angualrjs et webapi2. J'utilise le code de ligne ci-dessus, après le téléchargement du fichier, la page s'est bloquée et pendue. Au-dessous de la ligne de code, j'ai été utilisé mais j'obtenais une valeur nulle dans la méthode webapi. "var base64String = btoa (String.fromCharCode.apply (null, nouveau Uint8Array (arrayBuffer)));" veuillez suggérer une idée ...
prabhakaran S
102

Cela fonctionne bien pour moi:

var base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));

Dans ES6, la syntaxe est un peu plus simple:

let base64String = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));

Comme indiqué dans les commentaires, cette méthode peut entraîner une erreur d'exécution dans certains navigateurs lorsqu'elle ArrayBufferest volumineuse. La taille exacte dépend de l'implémentation dans tous les cas.

GOTO 0
la source
38
J'aime mieux cette méthode pour plus de concision, mais j'obtiens une "erreur de taille maximale de pile d'appels dépassée". La technique de boucle ci-dessus contourne cela.
Jay
13
Je reçois également une erreur de taille de pile, j'ai donc utilisé la réponse de mobz et cela a très bien fonctionné.
David Jones
12
Cela n'a pas fonctionné pour les grands tampons. Légère modification pour le faire fonctionner:btoa([].reduce.call(new Uint8Array(bufferArray),function(p,c){return p+String.fromCharCode(c)},''))
laggingreflex
1
J'essaie de télécharger un fichier pdf de 50 Mo en utilisant angualrjs et webapi2. J'utilise le code de ligne ci-dessus, après le téléchargement du fichier, la page s'est bloquée et pendue. Au-dessous de la ligne de code, j'ai été utilisé mais j'obtenais une valeur nulle dans la méthode webapi. "var base64String = btoa (String.fromCharCode.apply (null, nouveau Uint8Array (arrayBuffer)));" veuillez suggérer une idée ...
prabhakaran S
2
@Kugel btoaest sans danger pour les caractères dans la plage de code 0-255, comme c'est le cas ici (pensez au 8 pouces Uint8Array).
GOTO 0
42

Pour ceux qui l'aiment court, voici une autre utilisation Array.reducequi ne causera pas de débordement de pile:

var base64 = btoa(
  new Uint8Array(arrayBuffer)
    .reduce((data, byte) => data + String.fromCharCode(byte), '')
);
gkunz
la source
4
Je ne sais pas si c'est vraiment sexy. Après tout, vous créez de <amount of Bytes in the buffer>nouvelles chaînes.
Neonit
Et alors btoa(new Uint8Array(arraybuffer).reduce((data,byte)=>(data.push(String.fromCharCode(byte)),data),[]).join(''))?
Roy Tinker
35

Il existe une autre manière asynchrone d'utiliser Blob et FileReader.

Je n'ai pas testé la performance. Mais c'est une façon de penser différente.

function arrayBufferToBase64( buffer, callback ) {
    var blob = new Blob([buffer],{type:'application/octet-binary'});
    var reader = new FileReader();
    reader.onload = function(evt){
        var dataurl = evt.target.result;
        callback(dataurl.substr(dataurl.indexOf(',')+1));
    };
    reader.readAsDataURL(blob);
}

//example:
var buf = new Uint8Array([11,22,33]);
arrayBufferToBase64(buf, console.log.bind(console)); //"CxYh"
cuixiping
la source
Utilisez dataurl.split(',', 2)[1]au lieu de dataurl.substr(dataurl.indexOf(',')+1).
Carter Medlin
Cela ne semble pas garanti de fonctionner. Selon w3c.github.io/FileAPI/#issue-f80bda5b readAsDataURL pourrait théoriquement renvoyer un pourcentage codé dataURI (et il semble que ce soit effectivement le cas dans jsdom )
TS
@CarterMedlin Pourquoi serait splitmieux que substring?
TS
la division est plus courte. mais dataurl peut contenir une ou plusieurs virgules (,), le fractionnement n'est pas sûr.
cuixiping
12

J'ai utilisé cela et travaille pour moi.

function arrayBufferToBase64( buffer ) {
    var binary = '';
    var bytes = new Uint8Array( buffer );
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
        binary += String.fromCharCode( bytes[ i ] );
    }
    return window.btoa( binary );
}



function base64ToArrayBuffer(base64) {
    var binary_string =  window.atob(base64);
    var len = binary_string.length;
    var bytes = new Uint8Array( len );
    for (var i = 0; i < len; i++)        {
        bytes[i] = binary_string.charCodeAt(i);
    }
    return bytes.buffer;
}
Elias Vargas
la source
Pas sécurisé. Voir la réponse @chemoish
Kugel
11

Ma recommandation est de ne PAS utiliser de btoastratégies natives, car elles ne codent pas correctement toutes ArrayBuffer

réécrire les DOM atob () et btoa ()

Étant donné que DOMStrings sont des chaînes codées sur 16 bits, dans la plupart des navigateurs appelant window.btoa sur une chaîne Unicode provoquera une exception de caractère hors plage si un caractère dépasse la plage d'un caractère codé en ASCII 8 bits.

Bien que je n'aie jamais rencontré cette erreur exacte, j'ai constaté que la plupart des codes que ArrayBufferj'ai essayé de coder ne sont pas codés correctement.

J'utiliserais soit la recommandation MDN ou l'essentiel.

chimoish
la source
btoane fonctionne pas sur String, mais OP demande ArrayBuffer.
tsh
1
Très bien, tant d'extraits ici qui recommandent la mauvaise chose! J'ai vu cette erreur plusieurs fois, où les gens utilisent aveuglément atob et btoa.
Kugel
4
Tous les tampons de tableau doivent être codés correctement en utilisant les stratégies des autres réponses, atob / btoa n'est qu'un problème pour le texte qui contient des caractères supérieurs à 0xFF (ce qui n'est pas le cas des tableaux d'octets par définition). L'avertissement MDN ne s'applique pas car lorsque vous utilisez la stratégie dans les autres réponses, vous êtes assuré d'avoir une chaîne qui ne comprend que des caractères ASCII car toute valeur d'un Uint8Array est garantie comprise entre 0 et 255, ce qui signifie que String.fromCharCode est garanti pour retourner un caractère qui n'est pas hors de portée.
Jamesernator
11
var blob = new Blob([arrayBuffer])

var reader = new FileReader();
reader.onload = function(event){
   var base64 =   event.target.result
};

reader.readAsDataURL(blob);
Alex
la source
1
Ajoutez quelques explications à votre réponse s'il vous plaît. que veut dire ce code?
Oscar
Exemple MDN utile similaire .
spenceryue
1
c'est de loin l'approche la plus rapide - des dizaines de fois plus rapide que les autres lors de mes tests limités
jitin
j'aurais aimé retrouver cette solution comme 8 heures. ma journée n'aurait pas été gâchée; (merci
Kaiser Shahid
7

Voici 2 fonctions simples pour convertir Uint8Array en chaîne Base64 et vice-versa

arrayToBase64String(a) {
    return btoa(String.fromCharCode(...a));
}

base64StringToArray(s) {
    let asciiString = atob(s);
    return new Uint8Array([...asciiString].map(char => char.charCodeAt(0)));
}
Steve Dixon
la source
1
C'est une réponse déroutante. Cela ne ressemble pas à du JavaScript valide et est-ce qu'un Uint8Array est un ArrayBuffer?
user1153660
@ user1153660 Ajoutez le functionmot - clé et cela devrait fonctionner dans un navigateur moderne.
Yeti
Impressionnant! btoa (String.fromCharCode (... a)); est la version la plus courte que j'ai vue jusqu'à présent pour encoder Uint8Array.
Nicolo
1
Cela semble bon, mais si le tableau est trop grand, il générera une erreur de taille maximale de la pile d'appels.
Paweł Psztyć
0

Vous pouvez dériver un tableau normal de à l' ArrayBufferaide de Array.prototype.slice. Utilisez une fonction telle que Array.prototype.mapconvertir les octets en caractères et joinles convertir ensemble en une chaîne.

function arrayBufferToBase64(ab){

    var dView = new Uint8Array(ab);   //Get a byte view        

    var arr = Array.prototype.slice.call(dView); //Create a normal array        

    var arr1 = arr.map(function(item){        
      return String.fromCharCode(item);    //Convert
    });

    return window.btoa(arr1.join(''));   //Form a string

}

Cette méthode est plus rapide car il n'y a aucune concaténation de chaînes en cours d'exécution.

Charlie
la source
Pas sécurisé. Voir la réponse @chemoish
Kugel
0

L'OP n'a pas spécifié l'environnement d'exécution, mais si vous utilisez Node.JS, il existe un moyen très simple de le faire.

Accord avec les documents officiels Node.JS https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings

// This step is only necessary if you don't already have a Buffer Object
const buffer = Buffer.from(yourArrayBuffer);

const base64String = buffer.toString('base64');

De plus, si vous utilisez sous Angular par exemple, la classe de tampon sera également disponible dans un environnement de navigateur.

João Eduardo Soares e Silva
la source
Votre réponse ne s'applique qu'à NodeJS et ne fonctionnera pas dans le navigateur.
jvatic
1
@jvatic Je vois, l'OP n'a pas précisé clairement l'environnement d'exécution, donc ma réponse n'est pas incorrecte, il a seulement tagué Javascript. J'ai donc mis à jour ma réponse pour la rendre plus concise. Je pense que c'est une réponse importante parce que je cherchais comment faire et je n'ai pas pu trouver la meilleure réponse au problème.
João Eduardo Soares e Silva
-3

À mes côtés, en utilisant le navigateur Chrome, j'ai dû utiliser DataView () pour lire un arrayBuffer

function _arrayBufferToBase64( tabU8A ) {
var binary = '';
let lecteur_de_donnees = new DataView(tabU8A);
var len = lecteur_de_donnees.byteLength;
var chaine = '';
var pos1;
for (var i = 0; i < len; i++) {
    binary += String.fromCharCode( lecteur_de_donnees.getUint8( i ) );
}
chaine = window.btoa( binary )
return chaine;}
Louvet Jean
la source
-4
function _arrayBufferToBase64(uarr) {
    var strings = [], chunksize = 0xffff;
    var len = uarr.length;

    for (var i = 0; i * chunksize < len; i++){
        strings.push(String.fromCharCode.apply(null, uarr.subarray(i * chunksize, (i + 1) * chunksize)));
    }

    return strings.join("");
}

C'est mieux si vous utilisez JSZip pour décompresser l'archive de la chaîne

RouR
la source