Convierta la cadena base64 en ArrayBuffer

98

Necesito convertir una cadena de codificación base64 en un ArrayBuffer. Las cadenas base64 son entradas del usuario, se copiarán y pegarán desde un correo electrónico, por lo que no estarán allí cuando se cargue la página. Me gustaría hacer esto en javascript sin hacer una llamada ajax al servidor si es posible.

Encontré esos enlaces interesantes, pero no me ayudaron:

ArrayBuffer a cadena codificada en base64

se trata de la conversión opuesta, de ArrayBuffer a base64, no al revés

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

esto se ve bien pero no puedo entender cómo usar el código.

¿Existe una forma fácil (tal vez nativa) de realizar la conversión? Gracias

Tony
fuente

Respuestas:

147

Prueba esto:

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;
}
Goran.it
fuente
3
Por favor, explíqueme lo que realmente está sucediendo aquí.
Govi S
4
Bueno, es bastante sencillo, primero decodificamos la cadena base64 (atob), luego creamos una nueva matriz de enteros sin signo de 8 bits con la misma longitud que la cadena decodificada. Después de eso, iteramos la cadena y completamos la matriz con el valor Unicode de cada carácter de la cadena.
Goran.it
2
De MDN: Base64 es un grupo de esquemas de codificación de binario a texto similares que representan datos binarios en un formato de cadena ASCII traduciéndolos a una representación radix-64. La matriz con tipo Uint8Array representa una matriz de enteros sin signo de 8 bits, y estamos trabajando con la representación ASCII de los datos (que también es una tabla de 8 bits).
Goran.it
3
Esto no es correcto. Permite que javascript interprete bytes como cadenas, lo que afecta a los datos que en realidad son verdaderos binarios.
Tomáš Zato - Reincorpora a Monica
4
el problema es que a) no todas las secuencias de bytes son unicode válidas b) no todos los caracteres en unicode son de un byte, por lo que bytes[i] = binary_string.charCodeAt(i);pueden ser incorrectos
mezcla
52

Usando TypedArray. De :

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

El rendimiento se comparará con la versión de bucle for de la respuesta de Goran.it.

ofavre
fuente
2
A quien le guste este tipo de delineador, tenga en cuenta que Uint8Array.fromtodavía tiene poca compatibilidad con algunos navegadores.
IzumiSy
El compilador de rails no puede manejar esta cadena y falla con ExecJS::RuntimeError: SyntaxError: Unexpected token: operator (>); (carriles 5)
Avael Kross
3
Este no es un búfer de matriz. Esta es la matriz con tipo. Obtiene acceso al búfer de matriz a través de la .bufferpropiedad de lo que se devuelveUint8Array
oligofren
4
@Saites, no hay nada de malo en atobo btoa, solo tienes que darles una entrada válida. atobnecesita una cadena base64 válida, de lo contrario arrojará un error. Y btoanecesita una cadena de bytes válida (también llamada cadena binaria) que es una cadena que contiene caracteres en el rango 0-255. Si su cadena tiene caracteres fuera de ese rango, btoaarrojará un error.
GetFree
34

La respuesta de Goran.it no funciona debido a un problema de Unicode en javascript: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding .

Terminé usando la función dada en el blog de Daniel Guerrero: http://blog.danguer.com/2011/10/24/base64-binary-decoding-in-javascript/

La función se enumera en el enlace de github: https://github.com/danguer/blog-examples/blob/master/js/base64-binary.js

Use estas líneas

var uintArray = Base64Binary.decode(base64_string);  
var byteArray = Base64Binary.decodeArrayBuffer(base64_string); 
Yaan
fuente
1
Este método es 2 veces más rápido que usar atob.
xiaoyu2er
4
¿Puede dar un ejemplo en el que no funcionaría? El artículo habla sobre la codificación de cadenas arbitrarias, que pueden contener caracteres Unicode, pero no se aplica atoben absoluto.
riv
1
decodeArrayBufferdevuelve un ArrayBuffertamaño que siempre es divisible por 3, que no entiendo si es por diseño o por error. Preguntaré en el proyecto github.
ceztko
@ceztko Probablemente sea por diseño (accidental). El algoritmo de codificación base64 toma grupos de 3 bytes y los convierte en 4 caracteres. El método de decodificación probablemente asigna un ArrayBuffer cuya longitud es base64String.length / 4 * 3 bytes y nunca trunca los bytes no utilizados cuando termina.
AlwaysLearning
1
@AlwaysLearning, lo que significa que probablemente tenga errores, ya que los cero bytes restantes pueden dañar el contenido de salida previsto.
ceztko
21

Acabo de encontrar base64-arraybuffer, un pequeño paquete npm con un uso increíblemente alto, 5 millones de descargas el mes pasado (2017-08).

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

Para cualquiera que busque la mejor solución estándar, esta puede ser la solución.

jv-dev
fuente
9

Solución asincrónica , es mejor cuando los datos son grandes:

// 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);
}
张浩然
fuente
6

Javascript es un buen entorno de desarrollo, por lo que parece extraño que no proporcione una solución a este pequeño problema. Las soluciones que se ofrecen en otras partes de esta página son potencialmente lentas. Aquí está mi solución. Emplea la funcionalidad incorporada que decodifica las URL de datos de imagen y sonido 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 solicitud de envío falla si la cadena base 65 está mal formada.

El tipo mime (aplicación / octeto) probablemente sea innecesario.

Probado en cromo. Debería funcionar en otros navegadores.

dinosaurio trébol
fuente
1
Esta fue la solución perfecta para mí, simple y limpia. ¡Lo probé rápidamente en Firefox, IE 11, Edge y funcionó bien!
cs-NET
no se relaciona con la pregunta original
James Newton
No estoy seguro de cómo funciona para usted en IE11, pero aparece un Access Deniederror, que parece ser una limitación de CORS.
Sergiu
6

Para los usuarios de Node.js:

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

myBuffer será de tipo Buffer, que es una subclase de Uint8Array. Desafortunadamente, Uint8Array NO es un ArrayBuffer como pedía el OP. Pero cuando manipulo un ArrayBuffer, casi siempre lo envuelvo con Uint8Array o algo similar, por lo que debería estar cerca de lo que se solicita.

DoomGoober
fuente
2

Pure JS - sin cadena middlestep (sin atob)

Escribo la siguiente función que convierte base64 de manera directa (sin conversión a cadena en el paso medio). IDEA

  • obtener un fragmento de 4 caracteres base64
  • buscar el índice de cada carácter en el alfabeto base64
  • convertir el índice en un número de 6 bits (cadena binaria)
  • une cuatro números de 6 bits que dan un número de 24 bits (almacenado como cadena binaria)
  • dividir la cadena de 24 bits en tres de 8 bits y convertir cada uno en un número y almacenarlos en la matriz de salida
  • caso de esquina: si la cadena de entrada base64 termina con uno / dos caracteres =, elimine uno / dos números de la matriz de salida

La siguiente solución permite procesar grandes cadenas de entrada base64. Una función similar para convertir bytes a base64 sin btoa está AQUÍ

Kamil Kiełczewski
fuente
así que no faltan "."?
Gillsoft AB
Prueba en un navegador, no estoy seguro de que este sea el resultado esperado. "Alice's Adventure in Wonderland" (es decir, el último personaje es NaN)
Gillsoft AB
1
@GillsoftAB gracias por esta información - tienes razón - soluciono el problema
Kamil Kiełczewski
-3
const str = "dGhpcyBpcyBiYXNlNjQgc3RyaW5n"
const encoded = new TextEncoder().encode(str) // is Uint8Array
const buf = encoded.buffer // is ArrayBuffer
Andrii Nemchenko
fuente
6
Tenga en cuenta que esto no realiza ninguna decodificación / codificación Base64. Simplemente convierte los 6 bytes de "base64" en un ArrayBuffer o Uint8Array de 6 elementos.
dubek
2
@dubek eso es lo que se ha preguntado.
Andrii Nemchenko