Come convertire l'array uint8 in stringa codificata base64?


90

Ho una comunicazione webSocket, ricevo una stringa codificata base64, la converto in uint8 e ci lavoro, ma ora devo rispedire, ho ricevuto l'array uint8 e devo convertirlo in stringa base64, quindi posso inviarlo. Come posso fare questa conversione?



La domanda "ArrayBuffer a stringa codificata base64" contiene una soluzione migliore che gestisce tutti i caratteri. stackoverflow.com/questions/9267899/…
Steve Hanov il

Risposte:


16

Tutte le soluzioni già proposte presentano gravi problemi. Alcune soluzioni non funzionano su array di grandi dimensioni, alcune forniscono un output errato, alcune generano un errore sulla chiamata btoa se una stringa intermedia contiene caratteri multibyte, altre consumano più memoria del necessario.

Quindi ho implementato una funzione di conversione diretta che funziona indipendentemente dall'input. Converte circa 5 milioni di byte al secondo sulla mia macchina.

https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727


Avere base64abc come array di stringhe è più veloce del semplice renderlo una stringa? "ABCDEFG..."?
Garr Godfrey,

163

Se i tuoi dati possono contenere sequenze multibyte (non una semplice sequenza ASCII) e il tuo browser ha TextDecoder , allora dovresti usarlo per decodificare i tuoi dati (specifica la codifica richiesta per TextDecoder):

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

Se è necessario supportare browser che non dispongono di TextDecoder (attualmente solo IE ed Edge), l'opzione migliore è utilizzare un polyfill TextDecoder .

Se i tuoi dati contengono un semplice ASCII (non multibyte Unicode / UTF-8), allora c'è una semplice alternativa String.fromCharCodeche dovrebbe essere abbastanza universalmente supportata:

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

E per decodificare la stringa base64 in un Uint8Array:

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

Se si dispone di buffer di array molto grandi, l'applicazione potrebbe non riuscire e potrebbe essere necessario suddividere in blocchi il buffer (in base a quello pubblicato da @RohitSengar). Di nuovo, nota che questo è corretto solo se il tuo buffer contiene solo caratteri ASCII non multibyte:

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
Questo funziona per me in Firefox, ma Chrome si blocca con "Uncaught RangeError: Dimensione massima dello stack di chiamate superata" (facendo il btoa).
Michael Paulukonis

3
@MichaelPaulukonis la mia ipotesi è che in realtà sia String.fromCharCode.apply che sta causando il superamento della dimensione dello stack. Se si dispone di un Uint8Array molto grande, sarà probabilmente necessario creare in modo iterativo la stringa invece di utilizzare l'applicazione per farlo. La chiamata apply () sta passando ogni elemento del tuo array come parametro a fromCharCode, quindi se l'array è lungo 128000 byte, allora proveresti a fare una chiamata di funzione con 128000 parametri che probabilmente farà saltare lo stack.
kanaka

4
Grazie. Tutto ciò di cui avevo bisogno erabtoa(String.fromCharCode.apply(null, myArray))
Glen Little,

29
Questo non funziona se l'array di byte non è Unicode valido.
Melab

11
Non ci sono caratteri multibyte in una stringa base64 o in Uint8Array. TextDecoderè assolutamente la cosa sbagliata da usare qui, perché se il tuo Uint8Arrayha byte nell'intervallo 128..255, il decodificatore di testo li convertirà erroneamente in caratteri Unicode, il che interromperà il convertitore base64.
riv

26

Soluzione molto semplice e test per 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));

4
Soluzione più pulita!
realappie

Soluzione perfetta
Haris ur Rehman

2
fallisce su dati di grandi dimensioni (come le immagini) conRangeError: Maximum call stack size exceeded
Maxim Khokhryakov

21

Se stai usando Node.js, puoi usare questo codice per convertire Uint8Array in base64

var b64 = Buffer.from(u8).toString('base64');

4
Questa è una risposta migliore quindi le funzioni rotolate a mano sopra in termini di prestazioni.
Ben Liyanage

2
Eccezionale! Grazie. La migliore risposta di sempre
Alan

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

È possibile utilizzare questa funzione se si dispone di un Uint8Array molto grande. Questo è per Javascript, può essere utile in caso di FileReader readAsArrayBuffer.


2
È interessante notare che in Chrome l'ho cronometrato su un buffer di 300kb + e ho scoperto che farlo in blocchi come se fossi leggermente più lento di farlo byte per byte. Questo mi ha sorpreso.
Matt

@ Matt interessante. È possibile che nel frattempo Chrome abbia ora rilevato questa conversione e abbia un'ottimizzazione specifica per essa e la suddivisione in blocchi dei dati potrebbe ridurne l'efficienza.
kanaka

2
Non è sicuro, vero? Se il confine del mio blocco attraversa un carattere codificato UTF8 multibyte, allora fromCharCode () non sarebbe in grado di creare caratteri sensibili dai byte su entrambi i lati del confine, vero?
Jens

2
I String.fromCharCode.apply()metodi @ Jens non possono riprodurre UTF-8: i caratteri UTF-8 possono variare in lunghezza da un byte a quattro byte, tuttavia String.fromCharCode.apply()esamina un UInt8Array in segmenti di UInt8, quindi presume erroneamente che ogni carattere sia esattamente lungo un byte e indipendente dal vicino quelli. Se i caratteri codificati nell'input UInt8Array si trovano tutti nell'intervallo ASCII (byte singolo), funzionerà per caso, ma non può riprodurre UTF-8 completo. Hai bisogno di TextDecoder o di un algoritmo simile per questo.
Jamie Birch,

1
@ Jens quali caratteri multibyte codificati UTF8 in un array di dati binari? Non abbiamo a che fare con stringhe Unicode qui, ma con dati binari arbitrari, che NON dovrebbero essere trattati come punti di codice utf-8.
riv

0

Ecco una funzione JS per questo:

Questa funzione è necessaria perché Chrome non accetta una stringa con codifica base64 come valore per applicationServerKey in pushManager.subscribe ancora https://bugs.chromium.org/p/chromium/issues/detail?id=802280

function urlBase64ToUint8Array(base64String) {
  var padding = '='.repeat((4 - base64String.length % 4) % 4);
  var base64 = (base64String + padding)
    .replace(/\-/g, '+')
    .replace(/_/g, '/');

  var rawData = window.atob(base64);
  var outputArray = new Uint8Array(rawData.length);

  for (var i = 0; i < rawData.length; ++i) {
    outputArray[i] = rawData.charCodeAt(i);
  }
  return outputArray;
}

3
Questo converte base64 in Uint8Array. Ma la domanda chiede come convertire Uint8Array in base64
Barry Michael Doyle

0

Pure JS - no string middlestep (no btoa)

Nella soluzione sotto ometto la conversione in stringa. IDEA sta seguendo:

  • unisci 3 byte (3 elementi dell'array) e ottieni 24 bit
  • dividere 24 bit in quattro numeri a 6 bit (che assumono valori da 0 a 63)
  • usa quei numeri come indice nell'alfabeto base64
  • caso d'angolo: quando la matrice di byte di input la lunghezza non è divisa per 3, quindi aggiungere =o ==al risultato

La soluzione seguente funziona su blocchi di 3 byte, quindi è utile per array di grandi dimensioni. Una soluzione simile per convertire base64 in array binario (senza atob) è QUI


Mi piace la compattezza ma la conversione in stringhe che rappresentano un numero binario e poi indietro è molto più lenta della soluzione accettata.
Garr Godfrey,

0

Utilizzare quanto segue per convertire l'array uint8 in una stringa con codifica base64

function arrayBufferToBase64(buffer) {
            var binary = '';
            var bytes = [].slice.call(new Uint8Array(buffer));
            bytes.forEach((b) => binary += String.fromCharCode(b));
            return window.btoa(binary);
        };


-3

Se tutto ciò che desideri è un'implementazione JS di un codificatore base64, in modo da poter restituire i dati, puoi provare la btoafunzione.

b64enc = btoa(uint);

Un paio di brevi note su btoa: non è standard, quindi i browser non sono obbligati a supportarlo. Tuttavia, la maggior parte dei browser lo fa. Almeno quelli grandi. atobè la conversione opposta.

Se hai bisogno di un'implementazione diversa, o trovi un caso limite in cui il browser non ha idea di cosa stai parlando, cercare un codificatore base64 per JS non sarebbe troppo difficile.

Penso che ce ne siano 3 in giro sul sito web della mia azienda, per qualche motivo ...


Grazie, non l'ho mai provato prima.
Caio Keto

10
Coppia di appunti. btoa e atob fanno effettivamente parte del processo di standardizzazione HTML5 e la maggior parte dei browser li supporta già quasi allo stesso modo. In secondo luogo, btoa e atob funzionano solo con le stringhe. L'esecuzione di btoa su Uint8Array convertirà prima il buffer in una stringa utilizzando toString (). Ciò risulta nella stringa "[object Uint8Array]". Probabilmente non è quello che si intende.
kanaka

1
@CaioKeto potresti prendere in considerazione la possibilità di modificare la risposta selezionata. Questa risposta non è corretta.
kanaka

-4

npm installa google-closing-library --save

require("google-closure-library");
goog.require('goog.crypt.base64');

var result =goog.crypt.base64.encodeByteArray(Uint8Array.of(1,83,27,99,102,66));
console.log(result);

$node index.jsscriverebbe AVMbY2Y = sulla console.


1
È divertente che una -verisposta votata sia accettata piuttosto che altamente +ve.
Vishnudev
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.