Convertir une chaîne base64 en ArrayBuffer


98

J'ai besoin de convertir une chaîne d'encodage base64 en un ArrayBuffer. Les chaînes base64 sont entrées par l'utilisateur, elles seront copiées et collées à partir d'un e-mail, donc elles ne sont pas là lorsque la page est chargée. Je voudrais faire cela en javascript sans faire un appel ajax au serveur si possible.

J'ai trouvé ces liens intéressants, mais ils ne m'ont pas aidé:

ArrayBuffer en chaîne encodée en base64

il s'agit de la conversion opposée, de ArrayBuffer à base64, et non l'inverse

http://jsperf.com/json-vs-base64/2

cela semble bon mais je ne peux pas comprendre comment utiliser le code.

Existe-t-il un moyen simple (peut-être natif) de faire la conversion? Merci

Réponses:


147

Essaye ça:

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

3
Veuillez m'expliquer ce qui se passe réellement ici.
Govinda Sakhare

4
Eh bien, c'est assez simple, nous décodons d'abord la chaîne base64 (atob), puis nous créons un nouveau tableau d'entiers non signés 8 bits avec la même longueur que la chaîne décodée. Après cela, nous itérons la chaîne et remplissons le tableau avec la valeur Unicode de chaque caractère de la chaîne.
Goran.it

2
De MDN: Base64 est un groupe de schémas d'encodage binaire-texte similaires qui représentent des données binaires dans un format de chaîne ASCII en les traduisant en une représentation radix-64. Le tableau typé Uint8Array représente un tableau d'entiers non signés 8 bits, et nous travaillons avec une représentation ASCII des données (qui est également une table 8 bits) ..
Goran.it

3
Ce n'est pas correct. Il permet à javascript d'interpréter les octets comme une chaîne, ce qui affecte les données qui sont en fait du vrai binaire.
Tomáš Zato - Réintégrer Monica

4
le problème est que a) toutes les séquences d'octets ne sont pas valides unicode b) tous les caractères en unicode ne sont pas à un octet donc bytes[i] = binary_string.charCodeAt(i);peuvent être erronés
mélange du

52

Utilisation de TypedArray.from :

Uint8Array.from(atob(base64_string), c => c.charCodeAt(0))

Performances à comparer avec la version en boucle for de la réponse Goran.it.


2
Pour ceux qui aiment ce type de doublure, gardez à l'esprit qu'il Uint8Array.fromy a encore peu de compatibilité avec certains navigateurs.
IzumiSy

2
Veuillez ne pas recommander atob ou btoa: developer.mozilla.org/en-US/docs/Web/API/WindowBase64/…
Kugel

Le compilateur rails ne peut pas gérer cette chaîne et échoue avec ExecJS::RuntimeError: SyntaxError: Unexpected token: operator (>); (rails 5)
Avael Kross

3
Ce n'est pas un tampon de tableau. C'est le tableau typé. Vous avez accès au tampon du tableau via la .bufferpropriété de ce qui est renvoyé deUint8Array
oligofren

4
@Saites, il n'y a rien de mal avec atobou btoa, il suffit de leur donner une entrée valide. atoba besoin d'une chaîne base64 valide, sinon cela générera une erreur. Et a btoabesoin d'une chaîne d'octets valide (également appelée chaîne binaire) qui est une chaîne contenant des caractères compris entre 0 et 255. Si votre chaîne contient des caractères en dehors de cette plage, btoaune erreur sera générée.
GetFree

34

La réponse de Goran.it ne fonctionne pas en raison d'un problème Unicode en javascript - https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding .

J'ai fini par utiliser la fonction donnée sur le blog de Daniel Guerrero: http://blog.danguer.com/2011/10/24/base64-binary-decoding-in-javascript/

La fonction est répertoriée sur le lien github: https://github.com/danguer/blog-examples/blob/master/js/base64-binary.js

Utilisez ces lignes

var uintArray = Base64Binary.decode(base64_string);  
var byteArray = Base64Binary.decodeArrayBuffer(base64_string); 

1
Cette méthode est 2 fois plus rapide que l'utilisation d'atob.
xiaoyu2er

4
Pouvez-vous donner un exemple pour lequel cela ne fonctionnerait pas? L'article parle de l'encodage de chaînes arbitraires, qui peuvent contenir des caractères Unicode, mais ne s'appliquent pas atobdu tout.
riv

1
decodeArrayBufferrenvoie un ArrayBufferdont la taille est toujours divisible par 3, ce que je ne comprends pas si c'est par conception ou par un bug. Je vais demander dans le projet github.
ceztko

@ceztko C'est probablement par conception (accidentelle). L'algorithme de codage base64 prend des groupes de 3 octets et les transforme en 4 caractères. La méthode de décodage alloue probablement un ArrayBuffer dont la longueur est base64String.length / 4 * 3 octets et ne tronque jamais les octets inutilisés une fois terminé.
AlwaysLearning

1
@AlwaysLearning, ce qui signifie qu'il est probablement bogué car zéro octet restant peut corrompre le contenu de sortie prévu.
ceztko

21

Je viens de trouver base64-arraybuffer, un petit package npm avec une utilisation incroyablement élevée, 5 millions de téléchargements le mois dernier (2017-08).

https://www.npmjs.com/package/base64-arraybuffer

Pour ceux qui recherchent quelque chose d'une meilleure solution standard, c'est peut-être cela.


9

Solution asynchrone , c'est mieux lorsque les données sont volumineuses:

// base64 to buffer
function base64ToBufferAsync(base64) {
  var dataUrl = "data:application/octet-binary;base64," + base64;

  fetch(dataUrl)
    .then(res => res.arrayBuffer())
    .then(buffer => {
      console.log("base64 to buffer: " + new Uint8Array(buffer));
    })
}

// buffer to base64
function bufferToBase64Async( buffer ) {
    var blob = new Blob([buffer], {type:'application/octet-binary'});    
    console.log("buffer to blob:" + blob)

    var fileReader = new FileReader();
    fileReader.onload = function() {
      var dataUrl = fileReader.result;
      console.log("blob to dataUrl: " + dataUrl);

      var base64 = dataUrl.substr(dataUrl.indexOf(',')+1)      
      console.log("dataUrl to base64: " + base64);
    };
    fileReader.readAsDataURL(blob);
}

6

Javascript est un bon environnement de développement, il semble donc étrange qu'il ne fournit pas de solution à ce petit problème. Les solutions proposées ailleurs sur cette page sont potentiellement lentes. Voici ma solution. Il utilise la fonctionnalité intégrée qui décode les URL de données d'image et de son en base64.

var req = new XMLHttpRequest;
req.open('GET', "data:application/octet;base64," + base64Data);
req.responseType = 'arraybuffer';
req.onload = function fileLoaded(e)
{
   var byteArray = new Int8Array(e.target.response);
   // var shortArray = new Int16Array(e.target.response);
   // var unsignedShortArray = new Int16Array(e.target.response);
   // etc.
}
req.send();

La demande d'envoi échoue si la chaîne de base 65 est mal formée.

Le type mime (application / octet) est probablement inutile.

Testé en chrome. Devrait fonctionner dans d'autres navigateurs.


1
C'était la solution parfaite pour moi, simple et propre. Je l'ai rapidement testé dans Firefox, IE 11, Edge et a bien fonctionné!
cs-NET

ne concerne pas la question d'origine
James Newton

Je ne sais pas comment cela fonctionne pour vous dans IE11, mais j'obtiens une Access Deniederreur, qui semble être une limitation CORS.
Sergiu le

6

Pour les utilisateurs de Node.js:

const myBuffer = Buffer.from(someBase64String, 'base64');

myBuffer sera de type Buffer qui est une sous-classe de Uint8Array. Malheureusement, Uint8Array n'est PAS un ArrayBuffer comme l'OP le demandait. Mais lors de la manipulation d'un ArrayBuffer, je l'enveloppe presque toujours avec Uint8Array ou quelque chose de similaire, donc il devrait être proche de ce qui est demandé.


2

Pure JS - pas de chaîne intermédiaire (pas d'atob)

J'écris la fonction suivante qui convertit la base64 de manière directe (sans conversion en chaîne au pas moyen). IDÉE

  • obtenir un morceau de 4 caractères base64
  • trouver l'index de chaque caractère dans l'alphabet base64
  • convertir l'index en nombre 6 bits (chaîne binaire)
  • joindre quatre nombres de 6 bits qui donnent un nombre de 24 bits (stocké sous forme de chaîne binaire)
  • diviser la chaîne de 24 bits en trois chaînes de 8 bits et convertir chacune en un nombre et les stocker dans le tableau de sortie
  • cas d'angle: si la chaîne d'entrée base64 se termine par un / deux caractères =, supprimez un / deux nombres du tableau de sortie

La solution ci-dessous permet de traiter de grandes chaînes d'entrée base64. Une fonction similaire pour convertir des octets en base64 sans btoa est ICI


donc pas de "." manquant?
Gillsoft AB le

Test dans un navigateur, je ne suis pas sûr que ce soit le résultat attendu? "Alice's Adventure in Wonderland " (c'est-à-dire que le dernier personnage est NaN)
Gillsoft AB

1
@GillsoftAB merci pour cette info - vous avez raison - je corrige le problème
Kamil Kiełczewski

-3
const str = "dGhpcyBpcyBiYXNlNjQgc3RyaW5n"
const encoded = new TextEncoder().encode(str) // is Uint8Array
const buf = encoded.buffer // is ArrayBuffer

6
Notez que cela n'effectue aucun décodage / encodage Base64. Il transforme simplement les 6 octets de "base64" en un ArrayBuffer ou Uint8Array à 6 éléments.
dubek

2
@dubek c'est ce qui a été demandé.
Andrii Nemchenko
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.