ArrayBuffer a cadena codificada en base64


203

Necesito una forma eficiente (lectura nativa) para convertir una ArrayBuffercadena base64 que debe usarse en una publicación de varias partes.

Respuestas:


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

pero, las implementaciones no nativas son más rápidas, por ejemplo, https://gist.github.com/958841, consulte http://jsperf.com/encoding-xhr-image-data/6


10
Probé la implementación no nativa desde el enlace y me llevó 1 minuto y medio convertir un búfer de tamaño 1M, mientras que el código de bucle anterior solo tomó 1 segundo.
cshu

1
Me gusta la simplicidad de este enfoque, pero toda esa concatenación de cadenas puede ser costosa. Parece que construir una serie de personajes y join()ponerlos al final es significativamente más rápido en Firefox, IE y Safari (pero mucho más lento en Chrome): jsperf.com/tobase64-implementations
JLRishe

Estoy usando esta función para la conversión del búfer de matriz a la conversión de base64 pero no puedo recuperar el búfer de matriz. He escrito una función _base64ToArrayBuffer () aquí: codeshare.io/PT4pb pero eso me da un error como:Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
bawejakunal

esto no funciona este JSZip. encuentro otra manera github.com/michael/github/issues/137
RouR

1
Estoy intentando cargar archivos PDF de 50mb usando angualrjs y webapi2. Estoy usando el código de línea anterior, después de cargar el archivo, la página se bloqueó y se colgó. Debajo de la línea de código, estaba acostumbrado pero obtenía un valor nulo en el método webapi. "var base64String = btoa (String.fromCharCode.apply (nulo, nuevo Uint8Array (arrayBuffer)));" sugiera cualquier idea ...
prabhakaran S

102

Esto funciona bien para mi:

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

En ES6, la sintaxis es un poco más simple:

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

Como se señaló en los comentarios, este método puede provocar un error de tiempo de ejecución en algunos navegadores cuando ArrayBufferes grande. El límite de tamaño exacto depende de la implementación en cualquier caso.


38
Este método me gusta más por concisión, pero obtengo un "error de tamaño de pila de llamadas excedido". La técnica de bucle anterior evita eso.
Jay

13
También recibo un error de tamaño de pila, así que usé la respuesta de mobz y funcionó muy bien.
David Jones

12
No funcionó para grandes amortiguadores. Ligera modificación para que funcione:btoa([].reduce.call(new Uint8Array(bufferArray),function(p,c){return p+String.fromCharCode(c)},''))
laggingreflex

1
Estoy intentando cargar archivos PDF de 50mb usando angualrjs y webapi2. Estoy usando el código de línea anterior, después de cargar el archivo, la página se bloqueó y se colgó. Debajo de la línea de código, estaba acostumbrado pero obtenía un valor nulo en el método webapi. "var base64String = btoa (String.fromCharCode.apply (nulo, nuevo Uint8Array (arrayBuffer)));" sugiera cualquier idea ...
prabhakaran S

2
@ Kugel btoaes seguro para los caracteres en el rango de código 0-255, ya que este es el caso (piense en el 8 Uint8Array).
GOTO 0

42

Para aquellos a los que les gusta, este es otro Array.reduceque no causará desbordamiento de pila:

var base64 = btoa(
  new Uint8Array(arrayBuffer)
    .reduce((data, byte) => data + String.fromCharCode(byte), '')
);

44
No estoy seguro si eso es realmente sexy. Después de todo, estás creando <amount of Bytes in the buffer>nuevas cadenas.
Neonit

¿Qué tal btoa(new Uint8Array(arraybuffer).reduce((data,byte)=>(data.push(String.fromCharCode(byte)),data),[]).join(''))?
Roy Tinker

35

Hay otra forma asincrónica de usar Blob y FileReader.

No probé el rendimiento. Pero es una forma diferente de pensar.

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"

Usar en dataurl.split(',', 2)[1]lugar de dataurl.substr(dataurl.indexOf(',')+1).
Carter Medlin

Esto no parece estar garantizado para funcionar. De acuerdo con w3c.github.io/FileAPI/#issue-f80bda5b readAsDataURL podría teóricamente devolver un porcentaje de dataURI codificado (y parece que en realidad es el caso en jsdom )
TS

@CarterMedlin ¿Por qué sería splitmejor que substring?
TS

La división es más corta. pero dataurl puede contener una o más comas (,), la división no es segura.
Cuixiping

12

Usé esto y funciona para mí.

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

No es seguro. Ver respuesta @chemoish
Kugel

11

Mi recomendación para esto es NO usar btoaestrategias nativas , ya que no codifican correctamente todas ArrayBuffer...

reescribe los DOM atob () y btoa ()

Dado que DOMStrings son cadenas codificadas de 16 bits, en la mayoría de los navegadores que llaman a window.btoa en una cadena Unicode, se generará una excepción de caracteres fuera del rango si un carácter excede el rango de un carácter codificado ASCII de 8 bits.

Si bien nunca he encontrado este error exacto, he descubierto que muchos de los ArrayBufferque he intentado codificar se han codificado incorrectamente.

Yo usaría la recomendación MDN o lo esencial.


btoano funciona en String, pero OP pregunta ArrayBuffer.
tsh

1
Mucho esto, ¡tantos fragmentos aquí que recomiendan algo incorrecto! He visto este error varias veces, donde las personas usan ciegamente atob y btoa.
Kugel

44
Todos los búferes de matriz deben codificarse bien utilizando las estrategias en otras respuestas, atob / btoa es solo un problema para el texto que contiene caracteres mayores que 0xFF (que las matrices de bytes por definición no lo hacen). La advertencia de MDN no se aplica porque al usar la estrategia en las otras respuestas se garantiza que tendrá una cadena que solo consta de caracteres ASCII, ya que cualquier valor de un Uint8Array está garantizado entre 0 y 255, lo que significa que String.fromCharCode está garantizado. para devolver un personaje que no está fuera de rango.
Jamesernator

11
var blob = new Blob([arrayBuffer])

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

reader.readAsDataURL(blob);

1
Agregue alguna explicación a su respuesta por favor. ¿Qué significa este código?
Oscar

Ejemplo útil similar de MDN .
Spenceryue

1
esto es, con mucho, el método más rápido - decenas de veces más rápido que los demás en mi limitada de pruebas
Jitin

Desearía haber encontrado esta solución como 8 horas de nuevo. mi día no habría sido desperdiciado; (gracias
Kaiser Shahid

7

A continuación se presentan 2 funciones simples para convertir Uint8Array a String Base64 y viceversa

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

base64StringToArray(s) {
    let asciiString = atob(s);
    return new Uint8Array([...asciiString].map(char => char.charCodeAt(0)));
}

1
Esta es una respuesta confusa. ¿Eso no se ve como JavaScript válido y es un Uint8Array un ArrayBuffer?
user1153660

@ user1153660 Agregue la functionpalabra clave y debería funcionar en un navegador moderno.
Yeti

¡Increíble! btoa (String.fromCharCode (... a)); es la versión más corta que he visto hasta ahora para codificar Uint8Array.
Nicolo

1
Esto se ve bien, pero si la matriz es demasiado grande arrojará un error de tamaño de pila de llamadas máximo excedido.
Paweł Psztyć

0

Puede derivar una matriz normal del ArrayBuffermediante Array.prototype.slice. Utilice una función como Array.prototype.mapconvertir bytes en caracteres y joinunirlos en forma de cadena.

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

}

Este método es más rápido ya que no hay concatenaciones de cadenas ejecutándose en él.


No es seguro. Ver respuesta @chemoish
Kugel

0

El OP no especificó el entorno de ejecución, pero si está utilizando Node.JS, hay una manera muy sencilla de hacerlo.

Acordar con los documentos oficiales de 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');

Además, si está ejecutando en Angular, por ejemplo, la clase de búfer también estará disponible en un entorno de navegador.


Su respuesta solo se aplica a NodeJS y no funcionará en el navegador.
jvatic

1
@jvatic Ya veo, el OP no especificó claramente el entorno de ejecución, por lo que mi respuesta no es incorrecta, solo etiquetó Javascript. Así que actualicé mi respuesta para hacerlo más conciso. Creo que esta es una respuesta importante porque estaba buscando cómo hacer esto y no pude encontrar la mejor respuesta al problema.
João Eduardo Soares e Silva

-3

A mi lado, usando el navegador Chrome, tuve que usar DataView () para leer 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;}

-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("");
}

Esto es mejor si usa JSZip para descomprimir el archivo de la cadena

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.