ArrayBuffer ke base64 string yang disandikan

203

Saya membutuhkan cara (baca asli) yang efisien untuk mengonversi ArrayBufferstring ke base64 yang perlu digunakan pada pos multi-bagian.

zaheer
sumber

Jawaban:

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

tetapi, implementasi non-asli lebih cepat mis. https://gist.github.com/958841 lihat http://jsperf.com/encoding-xhr-image-data/6

mobz
sumber
10
Saya mencoba implementasi non-asli dari tautan dan butuh 1 menit setengah untuk mengkonversi buffer ukuran 1M sementara kode loop di atas hanya butuh 1sec.
cshu
1
Saya suka kesederhanaan dari pendekatan ini, tetapi semua rangkaian string itu bisa mahal. Sepertinya membangun array karakter dan join()menambahkannya pada akhirnya secara signifikan lebih cepat pada Firefox, IE, dan Safari (tetapi lebih lambat pada Chrome): jsperf.com/tobase64-implementations
JLRishe
Saya menggunakan fungsi ini untuk konversi array buffer ke base64 tapi saya tidak bisa mendapatkan kembali buffer array. Saya telah menulis fungsi _base64ToArrayBuffer () di sini: codeshare.io/PT4pb tetapi itu memberi saya kesalahan sebagai:Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
bawejakunal
ini tidak berfungsi JSZip ini. saya menemukan cara lain github.com/michael/github/issues/137
RouR
1
Saya mencoba unggah file pdf 50MB menggunakan angualrjs dan webapi2. Saya menggunakan kode baris di atas, setelah mengunggah file, halamannya mogok dan digantung. Di bawah baris kode, saya digunakan tetapi mendapatkan nilai nol dalam metode webapi. "var base64String = btoa (String.fromCharCode.apply (null, Uint8Array baru (arrayBuffer))));" tolong sarankan ide apa pun ...
prabhakaran S
102

Ini berfungsi baik untuk saya:

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

Dalam ES6, sintaks sedikit lebih sederhana:

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

Seperti yang ditunjukkan dalam komentar, metode ini dapat mengakibatkan kesalahan runtime di beberapa browser saat browser ArrayBufferberukuran besar. Batas ukuran yang tepat tergantung pada implementasi dalam hal apa pun.

GOTO 0
sumber
38
Saya suka metode ini lebih baik untuk keringkasan, tetapi mendapatkan "ukuran stack panggilan maksimum melebihi kesalahan". Teknik loop di atas mengatasi itu.
Jay
13
Saya juga mendapatkan kesalahan ukuran tumpukan, jadi saya menggunakan jawaban mobz dan itu bekerja dengan baik.
David Jones
12
Itu tidak berfungsi untuk buffer besar. Modifikasi ringan untuk membuatnya berfungsi:btoa([].reduce.call(new Uint8Array(bufferArray),function(p,c){return p+String.fromCharCode(c)},''))
laggingreflex
1
Saya mencoba unggah file pdf 50MB menggunakan angualrjs dan webapi2. Saya menggunakan kode baris di atas, setelah mengunggah file, halamannya mogok dan digantung. Di bawah baris kode, saya digunakan tetapi mendapatkan nilai nol dalam metode webapi. "var base64String = btoa (String.fromCharCode.apply (null, Uint8Array baru (arrayBuffer))));" tolong sarankan ide apa pun ...
prabhakaran S
2
@Kugel btoaaman untuk karakter dalam rentang kode 0-255, karena ini masalahnya (Pikirkan tentang 8 in Uint8Array).
GOTO 0
42

Bagi mereka yang suka itu pendek, berikut ini menggunakan Array.reduceyang lain yang tidak akan menyebabkan stack overflow:

var base64 = btoa(
  new Uint8Array(arrayBuffer)
    .reduce((data, byte) => data + String.fromCharCode(byte), '')
);
gkunz
sumber
4
Tidak yakin apakah itu benar-benar seksi. Lagi pula, Anda membuat <amount of Bytes in the buffer>string baru.
Neonit
Bagaimana dengan btoa(new Uint8Array(arraybuffer).reduce((data,byte)=>(data.push(String.fromCharCode(byte)),data),[]).join(''))?
Roy Tinker
35

Ada cara asinkron lain menggunakan Blob dan FileReader.

Saya tidak menguji kinerja. Tetapi cara berpikirnya berbeda.

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
sumber
Gunakan dataurl.split(',', 2)[1]sebagai ganti dataurl.substr(dataurl.indexOf(',')+1).
Carter Medlin
Ini sepertinya tidak dijamin berhasil. Menurut w3c.github.io/FileAPI/#issue-f80bda5b readAsDataURL secara teoritis dapat mengembalikan persen data yang disandikanURI (Dan tampaknya itu sebenarnya kasus di jsdom )
TS
@CarterMedlin Mengapa splitlebih baik daripada substring?
TS
perpecahan lebih pendek. tetapi dataurl dapat berisi satu atau lebih koma (,), pemisahan tidak aman.
cuixiping
12

Saya menggunakan ini dan bekerja untuk saya.

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
sumber
Tidak aman. Lihat @chemoish answer
Kugel
11

Rekomendasi saya untuk ini adalah BUKAN menggunakan btoastrategi asli — karena mereka tidak benar menyandikan semua ArrayBuffer...

tulis ulang DOM atob () dan btoa ()

Karena DOMStrings adalah string yang dikodekan 16-bit, di sebagian besar browser yang memanggil window.btoa pada string Unicode akan menyebabkan pengecualian Character Out Of Range jika karakter melebihi kisaran karakter yang dikodekan ASCII 8-bit.

Meskipun saya belum pernah menemukan kesalahan yang tepat ini, saya telah menemukan bahwa banyak dari yang ArrayBuffersaya coba enkode telah disandikan secara salah.

Saya akan menggunakan rekomendasi atau intisari MDN.

kemoterapi
sumber
btoatidak berfungsi pada String, tetapi OP bertanya ArrayBuffer.
tsh
1
Sangat banyak ini, begitu banyak cuplikan di sini yang merekomendasikan hal yang salah! Saya telah melihat kesalahan ini beberapa kali, di mana orang secara buta menggunakan atob dan btoa.
Kugel
4
Semua buffer array harus dikodekan dengan baik menggunakan strategi dalam jawaban lain, atob / btoa hanya masalah untuk teks yang berisi karakter lebih besar dari 0xFF (yang byte array menurut definisi tidak). Peringatan MDN tidak berlaku karena ketika menggunakan strategi di jawaban lain Anda dijamin memiliki string yang hanya terdiri dari karakter ASCII karena nilai apa pun dari Uint8Array dijamin antara 0 dan 255 yang berarti String.fromCharCode dijamin untuk mengembalikan karakter yang tidak di luar jangkauan.
Jamesernator
11
var blob = new Blob([arrayBuffer])

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

reader.readAsDataURL(blob);
Alex
sumber
1
Tolong tambahkan beberapa penjelasan untuk jawaban Anda. Apa arti kode ini?
Oscar
Contoh MDN bermanfaat yang serupa .
spenceryue
1
sejauh ini ini adalah pendekatan tercepat - puluhan kali lebih cepat daripada yang lain dalam pengujian terbatas saya
jitin
Saya berharap saya menemukan solusi ini seperti 8 jam lagi. hari saya tidak akan sia-sia; (terima kasih
Kaiser Shahid
7

Di bawah ini adalah 2 fungsi sederhana untuk mengonversi Uint8Array ke Base64 String dan kembali lagi

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

base64StringToArray(s) {
    let asciiString = atob(s);
    return new Uint8Array([...asciiString].map(char => char.charCodeAt(0)));
}
Steve Dixon
sumber
1
Ini jawaban yang membingungkan. Itu tidak terlihat seperti JavaScript yang valid dan apakah Uint8Array merupakan ArrayBuffer?
user1153660
@ user1153660 Tambahkan functionkata kunci dan itu harus berfungsi di browser modern.
Yeti
Luar biasa! btoa (String.fromCharCode (... a)); adalah versi terpendek yang saya lihat sejauh ini untuk menyandikan Uint8Array.
Nicolo
1
Ini terlihat bagus tetapi jika array terlalu besar akan melemparkan ukuran stack panggilan maksimum melebihi kesalahan.
Paweł Psztyć
0

Anda bisa mendapatkan array normal dari ArrayBuffermenggunakan Array.prototype.slice. Gunakan fungsi seperti Array.prototype.mapuntuk mengkonversi byte ke karakter dan joinmereka bersama-sama untuk membentuk string.

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

}

Metode ini lebih cepat karena tidak ada rangkaian string yang berjalan di dalamnya.

Charlie
sumber
Tidak aman. Lihat @chemoish answer
Kugel
0

OP tidak menentukan Running Enviroment tetapi jika Anda menggunakan Node.JS ada cara yang sangat sederhana untuk melakukan hal seperti itu.

Sesuai dengan dokumen Node.JS resmi 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');

Juga, Jika Anda menjalankan di bawah Angular misalnya, Kelas Buffer juga akan tersedia di Lingkungan Browser.

João Eduardo Soares e Silva
sumber
Jawaban Anda hanya berlaku untuk NodeJS dan tidak akan berfungsi di browser.
jvatic
1
@ jvatic Saya mengerti, OP tidak secara jelas menentukan Running Environment, jadi jawaban saya tidak salah, dia hanya menandai Javascript. Jadi saya memperbarui jawaban saya untuk membuatnya lebih ringkas. Saya pikir ini adalah jawaban yang penting karena saya mencari cara untuk melakukan ini dan tidak bisa mendapatkan jawaban terbaik untuk masalah ini.
João Eduardo Soares e Silva
-3

Di sebelah saya, menggunakan navigator Chrome, saya harus menggunakan DataView () untuk membaca 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
sumber
-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("");
}

Ini lebih baik, jika Anda menggunakan JSZip untuk membongkar arsip dari string

RouR
sumber