ArrayBuffer ถึง base64 สตริงที่เข้ารหัส


203

ฉันต้องการวิธีที่มีประสิทธิภาพ (อ่านพื้นเมือง) เพื่อแปลงArrayBufferเป็นสตริง base64 ซึ่งจำเป็นต้องใช้ในการโพสต์หลายส่วน

คำตอบ:


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

แต่การใช้งานที่ไม่ใช่เจ้าของภาษานั้นเร็วกว่าเช่นhttps://gist.github.com/958841 ดูที่http://jsperf.com/encoding-xhr-image-data/6


10
ฉันพยายามใช้งานที่ไม่ใช่เจ้าของภาษาจากลิงก์และใช้เวลา 1 นาทีครึ่งในการแปลงบัฟเฟอร์ขนาด 1M ในขณะที่โค้ดลูปข้างต้นใช้เวลาเพียง 1 วินาที
cshu

2
ฉันชอบความเรียบง่ายของวิธีนี้ แต่การต่อข้อมูลสตริงทั้งหมดนั้นอาจมีราคาสูง ดูเหมือนว่าการสร้างอาเรย์ของตัวละครและjoin()ท้ายที่สุดจะเร็วขึ้นอย่างมากบน Firefox, IE และ Safari (แต่ค่อนข้างช้ากว่ามากใน Chrome): jsperf.com/tobase64-implementations
JLRishe

ฉันกำลังใช้ฟังก์ชั่นนี้สำหรับการแปลงอาเรย์บัฟเฟอร์เป็นเบส 64 แต่ฉันไม่สามารถรับบัฟเฟอร์อาเรย์กลับมาได้ ฉันเขียนฟังก์ชัน _base64ToArrayBuffer () ที่นี่แล้ว: codeshare.io/PT4pbแต่นั่นทำให้ฉันมีข้อผิดพลาดเป็น:Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
bawejakunal

นี่ไม่ได้ทำงานกับ JSZip นี้ ฉันหาวิธีอื่นgithub.com/michael/github/issues/137
RouR

1
ฉันพยายามอัปโหลดไฟล์ PDF ขนาด 50mb โดยใช้ angualrjs และ webapi2 ฉันใช้รหัสบรรทัดด้านบนหลังจากอัปโหลดไฟล์หน้าเว็บล้มเหลวและแขวน บรรทัดด้านล่างของรหัสฉันถูกใช้ แต่รับค่า null ในวิธี webapi "var base64String = btoa (String.fromCharCode.apply (null ใหม่ Uint8Array (arrayBuffer)))"; กรุณาแนะนำความคิดใด ๆ ...
Prabhakaran S

103

มันใช้งานได้ดีสำหรับฉัน:

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

ใน ES6 ไวยากรณ์นั้นง่ายกว่าเล็กน้อย:

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

ตามที่ระบุไว้ในความคิดเห็นวิธีนี้อาจทำให้เกิดข้อผิดพลาดรันไทม์ในเบราว์เซอร์บางตัวเมื่อArrayBufferมีขนาดใหญ่ การ จำกัด ขนาดที่แน่นอนนั้นขึ้นอยู่กับการใช้งานในทุกกรณี


38
ฉันชอบวิธีนี้ดีกว่าสำหรับความรัดกุม แต่รับ "ข้อผิดพลาดการโทรเกินขนาดสูงสุด" เทคนิคการวนรอบด้านบนรับสิ่งนั้น
Jay

13
ฉันยังได้รับข้อผิดพลาดขนาดสแต็คดังนั้นฉันจึงใช้คำตอบของ mobz และใช้งานได้ดี
David Jones

12
มันไม่ทำงานสำหรับบัฟเฟอร์ขนาดใหญ่ การปรับเปลี่ยนเล็กน้อยเพื่อให้ทำงานได้:btoa([].reduce.call(new Uint8Array(bufferArray),function(p,c){return p+String.fromCharCode(c)},''))
laggingreflex

1
ฉันพยายามอัปโหลดไฟล์ PDF ขนาด 50mb โดยใช้ angualrjs และ webapi2 ฉันใช้รหัสบรรทัดด้านบนหลังจากอัปโหลดไฟล์หน้าเว็บล้มเหลวและแขวน บรรทัดด้านล่างของรหัสฉันถูกใช้ แต่รับค่า null ในวิธี webapi "var base64String = btoa (String.fromCharCode.apply (null ใหม่ Uint8Array (arrayBuffer)))"; กรุณาแนะนำความคิดใด ๆ ...
Prabhakaran S

2
@Kugel btoaมีความปลอดภัยสำหรับตัวอักษรในช่วงรหัส 0-255 เช่นนี้เป็นกรณี (คิดเกี่ยวกับ 8 ในUint8Array)
GOTO 0

42

สำหรับคนที่ชอบสั้น ๆ นี่คืออีกคนที่ใช้Array.reduceซึ่งจะไม่ทำให้เกิดการล้นสแต็ค:

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

4
ไม่แน่ใจว่ามันเซ็กซี่จริงๆหรือ <amount of Bytes in the buffer>ท้ายที่สุดคุณกำลังสร้างสตริงใหม่
Neonit

แล้วไงbtoa(new Uint8Array(arraybuffer).reduce((data,byte)=>(data.push(String.fromCharCode(byte)),data),[]).join(''))ล่ะ
Roy Tinker

35

มีอีกวิธีใช้แบบอะซิงโครนัสใช้ Blob และ FileReader

ฉันไม่ได้ทดสอบประสิทธิภาพ แต่มันเป็นวิธีคิดที่แตกต่าง

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"

ใช้แทนdataurl.split(',', 2)[1] dataurl.substr(dataurl.indexOf(',')+1)
Carter Medlin

1
ดูเหมือนจะไม่รับประกันว่าจะได้ผล ตามw3c.github.io/FileAPI/#issue-f80bda5b ใน readAsDataURLทางทฤษฎีสามารถคืนค่าการเข้ารหัสเปอร์เซ็นต์ datauri (และดูเหมือนว่าจริง ๆ แล้วมันเป็นjsdom )
TS

@CarterMedlin ทำไมจะsplitจะดีกว่าsubstring?
TS

แยกสั้น แต่ dataurl อาจมีเครื่องหมายจุลภาคหนึ่งรายการขึ้นไป (,) การแยกไม่ปลอดภัย
cuixiping

12

ฉันใช้มันและทำงานให้ฉัน

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

ไม่ปลอดภัย. ดูคำตอบ @chemoish
Kugel

11

คำแนะนำของฉันสำหรับสิ่งนี้คือการไม่ใช้btoaกลยุทธ์ดั้งเดิม- เนื่องจากไม่ได้เข้ารหัสอย่างถูกต้องทั้งหมดArrayBuffer...

เขียน DOMs atob () และ btoa () อีกครั้ง

เนื่องจาก DOMStrings เป็นสตริงที่มีการเข้ารหัส 16 บิตในเบราว์เซอร์ส่วนใหญ่ที่เรียก window.btoa บนสตริง Unicode จะทำให้เกิดข้อยกเว้น Character Out Of Range หากอักขระเกินช่วงของอักขระที่เข้ารหัส ASCII 8 บิต

ในขณะที่ฉันไม่เคยพบข้อผิดพลาดที่แน่นอนนี้ฉันพบว่ามีหลายคนที่ArrayBufferฉันพยายามเข้ารหัสได้เข้ารหัสอย่างไม่ถูกต้อง

ฉันจะใช้คำแนะนำหรือส่วนสำคัญของ MDN


btoaไม่ได้ทำงานบนเชือก แต่ OP ArrayBufferจะขอ
tsh

1
อย่างมากนี้มีตัวอย่างมากมายที่แนะนำสิ่งผิด! ฉันเห็นข้อผิดพลาดนี้หลายครั้งซึ่งผู้คนใช้ atob และ btoa อย่างสุ่ม ๆ
Kugel

4
บัฟเฟอร์อาร์เรย์ทั้งหมดควรเข้ารหัสอย่างดีโดยใช้กลยุทธ์ในคำตอบอื่น ๆ atob / btoa เป็นเพียงปัญหาของข้อความที่มีอักขระมากกว่า 0xFF (ซึ่งอาร์เรย์ไบต์ตามคำจำกัดความไม่ได้) คำเตือน MDN ไม่ได้ใช้เพราะเมื่อใช้กลยุทธ์ในคำตอบอื่น ๆ คุณรับประกันว่าจะมีสตริงที่ประกอบด้วยอักขระ ASCII เท่านั้นเป็นค่าใด ๆ จาก Uint8Array รับประกันว่าจะอยู่ระหว่าง 0 และ 255 ซึ่งหมายความว่ารับประกัน String.fromCharCode เพื่อคืนอักขระที่ไม่อยู่นอกระยะ
Jamesernator

11
var blob = new Blob([arrayBuffer])

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

reader.readAsDataURL(blob);

1
เพิ่มคำอธิบายให้กับคำตอบของคุณ รหัสนี้หมายถึงอะไร?
ออสการ์

ที่คล้ายกันที่เป็นประโยชน์เช่น MDN
spenceryue

1
นี่เป็นวิธีที่เร็วที่สุด - เร็วกว่าสิบเท่าในการทดสอบที่ จำกัด ของฉัน
jitin

ฉันหวังว่าฉันจะพบวิธีนี้เช่น 8 ชั่วโมงอีกครั้ง วันของฉันจะไม่สูญเปล่า (ขอบคุณ
ไกเซอร์ Shahid

7

ด้านล่างนี้เป็นฟังก์ชั่นง่าย ๆ สำหรับการแปลง Uint8Array เป็น Base64 String และกลับมาอีกครั้ง

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

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

1
นี่เป็นคำตอบที่สับสน มันดูไม่เหมือน JavaScript ที่ถูกต้องและเป็น Uint8Array an ArrayBuffer หรือไม่?
user1153660

@ user1153660 เพิ่มfunctionคำหลักและควรทำงานในเบราว์เซอร์ที่ทันสมัย
Yeti

! น่ากลัว btoa (String.fromCharCode ( ... ก)); เป็นรุ่นที่สั้นที่สุดที่ฉันเคยเห็นเพื่อเข้ารหัส Uint8Array
Nicolo

1
สิ่งนี้ดูดี แต่ถ้าอาร์เรย์มีขนาดใหญ่เกินไปมันจะโยนขนาดสแต็กการโทรสูงสุดเกินข้อผิดพลาด
PawełPsztyć

0

คุณสามารถได้รับอาร์เรย์ปกติจากโดยใช้ArrayBuffer Array.prototype.sliceใช้ฟังก์ชันที่ต้องการArray.prototype.mapแปลงไบต์เป็นอักขระและjoinรวมกันเป็นสตริง 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

}

วิธีนี้เร็วกว่าเนื่องจากไม่มีการต่อข้อมูลสตริง


ไม่ปลอดภัย. ดู @chemoish คำตอบ
Kugel

0

OP ไม่ได้ระบุสภาพแวดล้อมการทำงาน แต่ถ้าคุณใช้ Node.JS มีวิธีง่าย ๆ ในการทำสิ่งนั้น

สอดคล้องกับ Node.JS docs อย่างเป็นทางการ 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');

นอกจากนี้หากคุณกำลังทำงานภายใต้ Angular ตัวอย่างเช่น Buffer Class จะให้บริการในสภาพแวดล้อมของเบราว์เซอร์


คำตอบของคุณใช้ได้กับ NodeJS เท่านั้นและจะไม่ทำงานในเบราว์เซอร์
jvatic

1
@jvatic ฉันเห็น OP Javascriptไม่ได้อย่างชัดเจนระบุเล่นสิ่งแวดล้อมดังนั้นคำตอบของฉันไม่ได้ไม่ถูกต้องเขาแท็กเท่านั้น ดังนั้นฉันจึงปรับปรุงคำตอบเพื่อให้กระชับยิ่งขึ้น ฉันคิดว่านี่เป็นคำตอบที่สำคัญเพราะฉันกำลังค้นหาวิธีการทำสิ่งนี้และไม่สามารถหาคำตอบที่ดีที่สุดสำหรับปัญหาได้
João Eduardo Soares e Silva

-3

ข้างๆฉันเมื่อใช้การนำทาง Chrome ฉันต้องใช้ DataView () เพื่ออ่าน 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("");
}

นี่จะดีกว่าถ้าคุณใช้ JSZip เพื่อคลายการบีบอัดไฟล์จากสตริง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.