ArrayBuffer a cadena codificada en base64

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

mobz
fuente
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.

GOTO 0
fuente
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), '')
);
gkunz
fuente
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"
cuixiping
fuente
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;
}
Elias Vargas
fuente
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.

quimio
fuente
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);
Alex
fuente
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)));
}
Steve Dixon
fuente
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.

Charlie
fuente
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.

João Eduardo Soares e Silva
fuente
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;}
Louvet Jean
fuente
-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

RouR
fuente