ArrayBuffer su stringa codificata base64


203

Ho bisogno di un modo efficiente (leggi nativo) per convertire una ArrayBufferstringa in base64 che deve essere utilizzata su un post multipart.

Risposte:


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

ma le implementazioni non native sono più veloci, ad es. https://gist.github.com/958841 consultare http://jsperf.com/encoding-xhr-image-data/6


10
Ho provato l'implementazione non nativa dal collegamento e ci sono voluti 1 minuto e mezzo per convertire un buffer di dimensioni 1M mentre il codice del ciclo sopra ha richiesto solo 1 sec.
cshu,

1
Mi piace la semplicità di questo approccio, ma tutta quella concatenazione di stringhe può essere costosa. Sembra che costruire una serie di personaggi e join()ingenerarli alla fine sia significativamente più veloce su Firefox, IE e Safari (ma molto più lentamente su Chrome): jsperf.com/tobase64-implementations
JLRè

Sto usando questa funzione per la conversione da buffer di array a base64 ma non riesco a recuperare il buffer di array. Ho scritto una funzione _base64ToArrayBuffer () qui: codeshare.io/PT4pb ma questo mi dà un errore come:Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
bawejakunal

questo non ha funzionato questo JSZip. trovo un altro modo github.com/michael/github/issues/137
RouR

1
Sto provando il caricamento di file pdf da 50 MB usando angualrjs e webapi2. Sto usando il codice di linea sopra, dopo aver caricato il file, la pagina è stata bloccata e impiccata. Sotto la riga di codice, sono stato usato ma ho ottenuto un valore nullo nel metodo webapi. "var base64String = btoa (String.fromCharCode.apply (null, nuovo Uint8Array (arrayBuffer)));" per favore suggerisci qualche idea ...
prabhakaran S

102

Questo funziona bene per me:

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

In ES6, la sintassi è un po 'più semplice:

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

Come sottolineato nei commenti, questo metodo può causare un errore di runtime in alcuni browser quando ArrayBufferè grande. Il limite esatto di dimensione dipende comunque dall'implementazione.


38
Mi piace questo metodo meglio per la concisione, ma ottengo un "errore di dimensione massima dello stack di chiamate superato". La tecnica del loop sopra lo aggira.
Jay,

13
Ricevo anche un errore di dimensione dello stack, quindi ho usato la risposta di mobz e ha funzionato alla grande.
David Jones,

12
Non ha funzionato per buffer di grandi dimensioni. Leggera modifica per farlo funzionare:btoa([].reduce.call(new Uint8Array(bufferArray),function(p,c){return p+String.fromCharCode(c)},''))
laggingreflex

1
Sto provando il caricamento di file pdf da 50 MB usando angualrjs e webapi2. Sto usando il codice di linea sopra, dopo aver caricato il file, la pagina è stata bloccata e impiccata. Sotto la riga di codice, sono stato usato ma ottenendo un valore nullo nel metodo webapi. "var base64String = btoa (String.fromCharCode.apply (null, nuovo Uint8Array (arrayBuffer)));" per favore, suggerisci qualche idea ...
prabhakaran S

2
@Kugel btoaè sicuro per i caratteri nell'intervallo di codici 0-255, poiché questo è il caso (pensate agli 8 pollici Uint8Array).
GOTO 0

42

Per coloro a cui piace in breve, eccone un altro Array.reduceche non causerà l'overflow dello stack:

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

4
Non sono sicuro che sia davvero sexy. Dopo tutto, stai creando <amount of Bytes in the buffer>nuove stringhe.
Neonit,

Che ne dici btoa(new Uint8Array(arraybuffer).reduce((data,byte)=>(data.push(String.fromCharCode(byte)),data),[]).join(''))?
Roy Tinker,

35

Esiste un altro modo asincrono di utilizzare BLOB e FileReader.

Non ho testato le prestazioni. Ma è un modo diverso di pensare.

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"

Usa dataurl.split(',', 2)[1]invece di dataurl.substr(dataurl.indexOf(',')+1).
Carter Medlin,

Questo non sembra essere garantito per funzionare. Secondo w3c.github.io/FileAPI/#issue-f80bda5b readAsDataURL potrebbe teoricamente restituire un dato codificato in percentualeURI (E sembra che sia effettivamente il caso in jsdom )
TS

@CarterMedlin Perché sarebbe splitmeglio di substring?
TS,

la divisione è più breve. ma dataurl può contenere una o più virgole (,), la divisione non è sicura.
cuixiping,

12

L'ho usato e funziona per me.

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

Non sicuro. Vedi la risposta di @chemoish
Kugel il

11

La mia raccomandazione per questo è di NON utilizzare nativo btoa strategie , poiché non codificano correttamente tutteArrayBuffer ...

riscrivere i DOM atob () e btoa ()

Poiché DOMStrings sono stringhe con codifica a 16 bit, nella maggior parte dei browser che chiamano window.btoa su una stringa Unicode genererà un'eccezione Carattere fuori intervallo se un carattere supera l'intervallo di un carattere con codifica ASCII a 8 bit.

Anche se non ho mai riscontrato questo errore esatto, ho scoperto che molti dei ArrayBuffer che ho provato a codificare hanno codificato in modo errato.

Vorrei utilizzare la raccomandazione MDN o sostanza.


btoanon funziona su String, ma OP sta chiedendo ArrayBuffer.
TSH

1
Molto questo, così tanti frammenti qui che raccomandano la cosa sbagliata! Ho visto questo errore più volte, in cui le persone usano ciecamente atob e btoa.
Kugel,

4
Tutti i buffer di array devono essere codificati bene usando le strategie in altre risposte, atob / btoa è solo un problema per il testo che contiene caratteri maggiori di 0xFF (i quali array di byte per definizione non lo fanno). L'avviso MDN non si applica perché quando si utilizza la strategia nelle altre risposte si ha la certezza di avere una stringa composta solo da caratteri ASCII poiché qualsiasi valore di un Uint8Array è garantito tra 0 e 255, il che significa che String.fromCharCode è garantito per restituire un personaggio che non è fuori portata.
Jamesernator,

11
var blob = new Blob([arrayBuffer])

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

reader.readAsDataURL(blob);

1
Aggiungi qualche spiegazione alla tua risposta, per favore. Cosa significa questo codice?
Oscar


1
questo è di gran lunga l'approccio più veloce - decine di volte più veloce degli altri nei miei test limitati
jitin

Vorrei aver trovato questa soluzione ancora 8 ore. la mia giornata non sarebbe stata sprecata; (grazie
Kaiser Shahid,

7

Di seguito sono riportate 2 semplici funzioni per convertire Uint8Array in Base64 String e viceversa

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

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

1
Questa è una risposta confusa. Non sembra un JavaScript valido ed è un Uint8Array un ArrayBuffer?
user1153660

@ user1153660 Aggiungi la functionparola chiave e dovrebbe funzionare in un browser moderno.
Yeti,

Eccezionale! btoa (String.fromCharCode (... a)); è la versione più corta che ho visto finora per codificare Uint8Array.
Nicolo,

1
Questo sembra buono ma se l'array è troppo grande genererà un errore nella dimensione massima dello stack delle chiamate superato.
Paweł Psztyć,

0

È possibile derivare un array normale ArrayBufferda usando Array.prototype.slice. Utilizzare una funzione come Array.prototype.mapconvertire i byte in caratteri e joininsieme in stringa forma.

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

}

Questo metodo è più veloce poiché non ci sono concatenazioni di stringhe in esecuzione in esso.


Non sicuro. Vedi la risposta di @chemoish
Kugel,

0

L'OP non ha specificato l'ambiente in esecuzione ma se si utilizza Node.JS esiste un modo molto semplice per eseguire tale operazione.

Secondo i documenti ufficiali di 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');

Inoltre, se si esegue ad esempio in Angular, la classe buffer sarà resa disponibile anche in un ambiente browser.


La tua risposta si applica solo a NodeJS e non funzionerà nel browser.
jvatic

1
@jvatic, vedo, l'OP non ha specificato chiaramente l'ambiente in esecuzione, quindi la mia risposta non è errata, ha solo taggato Javascript. Quindi ho aggiornato la mia risposta per renderla più concisa. Penso che questa sia una risposta importante perché stavo cercando come farlo e non sono riuscito a ottenere la migliore risposta al problema.
João Eduardo Soares e Silva,

-3

Al mio fianco, usando Chrome Navigator, ho dovuto usare DataView () per leggere 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("");
}

Questo è meglio se usi JSZip per decomprimere l'archivio dalla stringa

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.