ArrayBuffer zu Base64-codierter Zeichenfolge

203

Ich brauche eine effiziente (native lesen) Methode, um eine ArrayBufferin eine base64-Zeichenfolge zu konvertieren , die für einen mehrteiligen Beitrag verwendet werden muss.

zaheer
quelle

Antworten:

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

Nicht native Implementierungen sind jedoch schneller, z. B. https://gist.github.com/958841, siehe http://jsperf.com/encoding-xhr-image-data/6

mobz
quelle
10
Ich habe die nicht native Implementierung über den Link ausprobiert und es dauerte anderthalb Minuten, um einen Puffer mit einer Größe von 1 MB zu konvertieren, während der obige Schleifencode nur 1 Sekunde dauerte.
Cshu
1
Ich mag die Einfachheit dieses Ansatzes, aber all diese Verkettung von Zeichenfolgen kann kostspielig sein. Es sieht so aus, als ob dasjoin()
Erstellen
Ich verwende diese Funktion für die Konvertierung von Array-Puffer in Base64, kann den Array-Puffer jedoch nicht zurückerhalten. Ich habe eine _base64ToArrayBuffer () Funktion hier wrriten: codeshare.io/PT4pb aber das gibt mir einen Fehler wie:Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
bawejakunal
Dies funktioniert in diesem JSZip nicht. Ich finde einen anderen Weg github.com/michael/github/issues/137
RouR
1
Ich versuche, 50 MB PDF-Datei mit angualrjs und webapi2 hochzuladen. Ich verwende den obigen Zeilencode. Nach dem Hochladen der Datei ist die Seite abgestürzt und hängt. Unterhalb der Codezeile wurde ich verwendet, aber in der Webapi-Methode wurde ein Nullwert angezeigt. "var base64String = btoa (String.fromCharCode.apply (null, neues Uint8Array (arrayBuffer)));" Bitte schlagen Sie eine Idee vor ...
Prabhakaran S
101

Das funktioniert gut für mich:

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

In ES6 ist die Syntax etwas einfacher:

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

Wie in den Kommentaren erwähnt, kann diese Methode in einigen Browsern zu einem Laufzeitfehler führen, wenn der ArrayBuffergroß ist. Die genaue Größenbeschränkung ist in jedem Fall implementierungsabhängig.

GOTO 0
quelle
38
Ich mag diese Methode aus Gründen der Übersichtlichkeit besser, erhalte aber den Fehler "Maximale Größe des Aufrufstapels überschritten". Die obige Schleifentechnik umgeht das.
Jay
13
Ich bekomme auch einen Stapelgrößenfehler, also habe ich die Antwort von mobz verwendet und es hat großartig funktioniert.
David Jones
12
Bei großen Puffern hat es nicht funktioniert. Leichte Modifikation, damit es funktioniert:btoa([].reduce.call(new Uint8Array(bufferArray),function(p,c){return p+String.fromCharCode(c)},''))
Laggingreflex
1
Ich versuche, 50 MB PDF-Datei mit angualrjs und webapi2 hochzuladen. Ich verwende den obigen Zeilencode. Nach dem Hochladen der Datei ist die Seite abgestürzt und hängt. Unterhalb der Codezeile wurde ich verwendet, aber in der Webapi-Methode wurde ein Nullwert angezeigt. "var base64String = btoa (String.fromCharCode.apply (null, neues Uint8Array (arrayBuffer)));" Bitte schlagen Sie eine Idee vor ...
Prabhakaran S
2
@Kugel btoaist sicher für Zeichen im Codebereich 0-255, da dies hier der Fall ist (Denken Sie an die 8 Zoll Uint8Array).
GOTO 0
42

Für diejenigen, die es kurz mögen, ist hier eine andere, Array.reducedie keinen Stapelüberlauf verursacht:

var base64 = btoa(
  new Uint8Array(arrayBuffer)
    .reduce((data, byte) => data + String.fromCharCode(byte), '')
);
gkunz
quelle
4
Ich bin mir nicht sicher, ob das wirklich sexy ist. Schließlich erstellen Sie <amount of Bytes in the buffer>neue Zeichenfolgen.
Neonit
Wie wäre es btoa(new Uint8Array(arraybuffer).reduce((data,byte)=>(data.push(String.fromCharCode(byte)),data),[]).join(''))?
Roy Tinker
35

Es gibt eine andere asynchrone Möglichkeit, Blob und FileReader zu verwenden.

Ich habe die Leistung nicht getestet. Aber es ist eine andere Denkweise.

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
quelle
Verwenden Sie dataurl.split(',', 2)[1]anstelle von dataurl.substr(dataurl.indexOf(',')+1).
Carter Medlin
Dies scheint nicht garantiert zu funktionieren. Laut w3c.github.io/FileAPI/#issue-f80bda5b readAsDataURL könnte theoretisch ein Prozent der codierten Daten zurückgegeben werdenURUR (und es scheint, dass dies in jsdom tatsächlich der Fall ist )
TS
@CarterMedlin Warum wäre splitbesser als substring?
TS
Split ist kürzer. dataurl kann jedoch ein oder mehrere Kommas (,) enthalten, split ist nicht sicher.
Cuixiping
12

Ich habe das benutzt und arbeite für mich.

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
quelle
Nicht sicher. Siehe @chemoish Antwort
Kugel
11

Meine Empfehlung hierfür ist, KEINE nativen btoaStrategien zu verwenden, da diese nicht alle ArrayBuffer

Schreiben Sie die DOMs atob () und btoa () neu.

Da es sich bei DOMStrings um 16-Bit-codierte Zeichenfolgen handelt, führt in den meisten Browsern, die window.btoa für eine Unicode-Zeichenfolge aufrufen, eine Ausnahme für Zeichen außerhalb des Bereichs aus, wenn ein Zeichen den Bereich eines 8-Bit-ASCII-codierten Zeichens überschreitet.

Obwohl ich diesen genauen Fehler noch nie festgestellt habe, habe ich festgestellt, dass viele der von ArrayBuffermir versuchten Codierungen falsch codiert wurden.

Ich würde entweder die MDN-Empfehlung oder das Wesentliche verwenden.

chemisch
quelle
btoafunktioniert nicht mit String, aber OP fragt ArrayBuffer.
tsh
1
Sehr viel davon, so viele Schnipsel hier, die das Falsche empfehlen! Ich habe diesen Fehler mehrmals gesehen, wo Leute blind atob und btoa benutzen.
Kugel
4
Alle Array-Puffer sollten unter Verwendung der Strategien in anderen Antworten fein codiert werden. Atob / btoa ist nur ein Problem für Text, der Zeichen größer als 0xFF enthält (was Byte-Arrays per Definition nicht tun). Die MDN-Warnung gilt nicht, da bei Verwendung der Strategie in den anderen Antworten garantiert wird, dass eine Zeichenfolge nur aus ASCII-Zeichen besteht, da ein Wert aus einem Uint8Array garantiert zwischen 0 und 255 liegt, was bedeutet, dass String.fromCharCode garantiert ist um ein Zeichen zurückzugeben, das nicht außerhalb des Bereichs liegt.
Jamesernator
11
var blob = new Blob([arrayBuffer])

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

reader.readAsDataURL(blob);
Alex
quelle
1
Fügen Sie Ihrer Antwort bitte eine Erklärung hinzu. Was bedeutet dieser Code?
Oscar
Ähnliches hilfreiches MDN-Beispiel .
Spenceryue
1
Dies ist bei weitem der schnellste Ansatz - zehnmal schneller als die anderen in meinen begrenzten Tests
am
Ich wünschte, ich hätte diese Lösung wieder wie 8 Stunden gefunden. Mein Tag wäre nicht verschwendet worden (danke
Kaiser Shahid
7

Im Folgenden finden Sie zwei einfache Funktionen zum Konvertieren von Uint8Array in Base64 String und wieder zurück

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

base64StringToArray(s) {
    let asciiString = atob(s);
    return new Uint8Array([...asciiString].map(char => char.charCodeAt(0)));
}
Steve Dixon
quelle
1
Dies ist eine verwirrende Antwort. Das sieht nicht nach gültigem JavaScript aus und ist ein Uint8Array ein ArrayBuffer?
user1153660
@ user1153660 Fügen Sie das functionSchlüsselwort hinzu und es sollte in einem modernen Browser funktionieren.
Yeti
Genial! btoa (String.fromCharCode (... a)); ist die kürzeste Version, die ich bisher gesehen habe, um Uint8Array zu codieren.
Nicolo
1
Das sieht gut aus, aber wenn das Array zu groß ist, wird die maximale Größe des Aufrufstapels überschritten.
Paweł Psztyć
0

Sie können ein normales Array aus dem ableiten, ArrayBufferindem Sie verwenden Array.prototype.slice. Verwenden Sie eine Funktion Array.prototype.map, um Bytes in Zeichen umzuwandeln und joindiese zusammen in eine Zeichenfolge umzuwandeln .

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

}

Diese Methode ist schneller, da keine Zeichenfolgenverkettungen ausgeführt werden.

Charlie
quelle
Nicht sicher. Siehe @chemoish Antwort
Kugel
0

Das OP hat die laufende Umgebung nicht angegeben, aber wenn Sie Node.JS verwenden, gibt es eine sehr einfache Möglichkeit, dies zu tun.

In Übereinstimmung mit den offiziellen Node.JS-Dokumenten 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');

Wenn Sie beispielsweise unter Angular ausgeführt werden, wird die Pufferklasse auch in einer Browserumgebung verfügbar gemacht.

João Eduardo Soares und Silva
quelle
Ihre Antwort gilt nur für NodeJS und funktioniert nicht im Browser.
Jvatic
1
@jvatic Ich sehe, das OP hat die Laufumgebung nicht eindeutig angegeben, daher ist meine Antwort nicht falsch, er hat nur getaggt Javascript. Deshalb habe ich meine Antwort aktualisiert, um sie präziser zu gestalten. Ich denke, dies ist eine wichtige Antwort, da ich nach einer Möglichkeit gesucht habe und nicht die beste Antwort auf das Problem finden konnte.
João Eduardo Soares und Silva
-3

An meiner Seite musste ich mit Chrome Navigator DataView () verwenden, um einen ArrayBuffer zu lesen

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

Dies ist besser, wenn Sie JSZip zum Entpacken des Archivs aus einer Zeichenfolge verwenden

RouR
quelle