2012-10-03 37 views
46

Obtuve una comunicación webSocket, recibí una cadena codificada en base64, la convertí en uint8 y trabajé en ella, pero ahora necesito enviar de vuelta, obtuve la matriz uint8, y necesito convertirla a una cadena base64, para poder enviar eso. ¿Cómo puedo hacer esta conversión?Cómo convertir uint8 Array a base64 Cadena codificada?

+0

[implementaciones MDN para Uint8array/ArrayBuffer <-> base64.] (Https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding#Solution_.232_.E2.80.93_rewriting_atob%28%29_and_btoa% 28% 29_using_TypedArrays_and_UTF-8) – JLRishe

Respuesta

106

Si su navegador ha TextDecoder luego usar esa:

var u8 = new Uint8Array([65, 66, 67, 68]); 
var decoder = new TextDecoder('utf8'); 
var b64encoded = btoa(decoder.decode(u8)); 

Si necesita apoyar browsers that do not have TextDecoder (actualmente solo IE y Edge), entonces la mejor opción es usar un TextDecoder polyfill.

Si sus cadenas ASCII es sencillo y no Unicode multibyte/UTF-8 luego hay es una alternativa sencilla utilizando String.fromCharCode que debe ser bastante respaldo universal:

var u8 = new Uint8Array([65, 66, 67, 68]); 
var b64encoded = btoa(String.fromCharCode.apply(null, u8)); 

Y para decodificar la cadena de base 64 hacia atrás a un Uint8Array:

var u8_2 = new Uint8Array(atob(b64encoded).split("").map(function(c) { 
    return c.charCodeAt(0); })); 

Si usted tiene muy grandes memorias intermedias de matriz entonces el aplicarán pueden fallar y puede que tenga que trozo de la memoria intermedia (basado en el publicado por @RohitSengar). Una vez más, tenga en cuenta que esto sólo es correcta si su memoria intermedia sólo contiene caracteres que no son de varios bytes ASCII:

function Uint8ToString(u8a){ 
    var CHUNK_SZ = 0x8000; 
    var c = []; 
    for (var i=0; i < u8a.length; i+=CHUNK_SZ) { 
    c.push(String.fromCharCode.apply(null, u8a.subarray(i, i+CHUNK_SZ))); 
    } 
    return c.join(""); 
} 
// Usage 
var u8 = new Uint8Array([65, 66, 67, 68]); 
var b64encoded = btoa(Uint8ToString(u8)); 
+4

Esta debería ser la respuesta aceptada. – jutky

+0

Perfecto, realmente funciona. –

+3

Esto funciona para mí en Firefox, pero Chrome se bloquea con "RangeError no capturado: se excedió el tamaño máximo de la pila de llamadas" (haciendo la btoa). –

-3

Si todo lo que quiere es una implementación JS de un codificador base64, para que pueda enviar datos de vuelta, puede probar la función btoa.

b64enc = btoa(uint); 

Un par de notas rápidas en btoa - es no estándar, por lo que los navegadores no están obligados a apoyarla. Sin embargo, la mayoría de los navegadores sí. Los grandes, al menos. atob es la conversión opuesta.

Si necesita una implementación diferente, o si encuentra un caso de borde donde el navegador no tiene idea de lo que está hablando, la búsqueda de un codificador base64 para JS no sería demasiado difícil.

creo que hay 3 de ellos dando vueltas en la página web de mi empresa, por alguna razón ...

+0

Gracias, no lo intenté antes. –

+10

Par de notas. btoa y atob son en realidad parte del proceso de estandarización de HTML5 y la mayoría de los navegadores sí los admiten en su mayoría de la misma manera. En segundo lugar, btoa y atob funcionan solo con cadenas. La ejecución de btoa en Uint8Array primero convertirá el búfer a una cadena usando toString(). Esto da como resultado la cadena "[objeto Uint8Array]". Probablemente no sea lo que se pretende. – kanaka

+1

@CaioKeto es posible que desee considerar cambiar la respuesta seleccionada. Esta respuesta no es correcta – kanaka

13
function Uint8ToBase64(u8Arr){ 
    var CHUNK_SIZE = 0x8000; //arbitrary number 
    var index = 0; 
    var length = u8Arr.length; 
    var result = ''; 
    var slice; 
    while (index < length) { 
    slice = u8Arr.subarray(index, Math.min(index + CHUNK_SIZE, length)); 
    result += String.fromCharCode.apply(null, slice); 
    index += CHUNK_SIZE; 
    } 
    return btoa(result); 
} 

Puede usar esta función si tiene un Uint8Array muy grande. Esto es para Javascript, puede ser útil en el caso de FileReader readAsArrayBuffer.

+2

Curiosamente, en Chrome cronometré esto en un buffer de 300kb + y encontré hacerlo en fragmentos como usted, para ser siempre un poco más lento que hacerlo byte a byte. Esto me sorprendió – Matt

+0

@Matt interesante. Es posible que, mientras tanto, Chrome haya detectado esta conversión y tenga una optimización específica para ella, y la fragmentación de los datos puede reducir su eficacia. – kanaka

+2

Esto no es seguro, ¿verdad? Si el límite de mi fragmento corta un carácter codificado en UTF8 de múltiples bytes, entonces [fromCharCode()] (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode) no sería capaz de crear caracteres sensibles a partir de los bytes en ambos lados del límite, ¿verdad? – Jens

7

¡Solución y prueba muy simples para JavaScript!

ToBase64 = function (u8) { 
    return btoa(String.fromCharCode.apply(null, u8)); 
} 

FromBase64 = function (str) { 
    return atob(str).split('').map(function (c) { return c.charCodeAt(0); }); 
} 

var u8 = new Uint8Array(256); 
for (var i = 0; i < 256; i++) 
    u8[i] = i; 

var b64 = ToBase64(u8); 
console.debug(b64); 
console.debug(FromBase64(b64)); 
+0

¡La solución más limpia! – Abdel

0

Voy a agregar otra solución que funcione con rangos no imprimibles. Supongo que esto es más rápido que encadenar TextEncoder y btoa.

var blob = new Blob([ uint8ArrayBuffer ], { type: "image/jpeg" }); 
var imageUrl = URL.createObjectURL(blob); 

Esto está utilizando API HTML5, por lo que no funcionará en Node u otros servidores basados ​​en JS, por supuesto. Puede ver una demostración here.

Cuestiones relacionadas