ArrayBuffer thành chuỗi được mã hóa base64


203

Tôi cần một cách hiệu quả (đọc bản địa) để chuyển đổi một ArrayBufferchuỗi cơ sở64 cần được sử dụng trên một bài đăng nhiều phần.

Câu trả lời:


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

tuy nhiên, việc triển khai không phải là bản địa nhanh hơn, ví dụ: https://gist.github.com/958841 xem http://jsperf.com/encoding-xhr-image-data/6


10
Tôi đã thử triển khai không phải bản địa từ liên kết và phải mất 1 phút rưỡi để chuyển đổi bộ đệm kích thước 1M trong khi mã vòng lặp ở trên chỉ mất 1 giây.
cshu

1
Tôi thích sự đơn giản của phương pháp này, nhưng tất cả các chuỗi nối đó có thể tốn kém. Có vẻ như việc xây dựng một loạt các ký tự và join()đưa chúng vào cuối nhanh hơn đáng kể trên Firefox, IE và Safari (nhưng chậm hơn rất nhiều trên Chrome): jsperf.com/tobase64-im THỰCations
JLRishe

Tôi đang sử dụng chức năng này cho bộ đệm mảng để chuyển đổi base64 nhưng tôi không thể lấy lại bộ đệm mảng. Tôi đã viết một hàm _base64ToArrayBuffer () tại đây: codeshare.io/PT4pb nhưng điều đó mang lại cho tôi một lỗi như:Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
bawejakunal 11/07/2015

cái này không hoạt động được cái này. tôi tìm một cách khác github.com/michael/github/issues/137
RouR

1
Tôi đang thử tải lên tệp pdf 50mb bằng angualrjs và webapi2. Tôi đang sử dụng mã dòng trên, sau khi tải lên tệp, trang bị lỗi và bị treo. Bên dưới dòng mã, tôi đã được sử dụng nhưng nhận được giá trị null trong phương thức webapi. "var base64String = btoa (String.fromCharCode.apply (null, Uint8Array mới (mảngBuffer)));" vui lòng đề xuất bất kỳ ý tưởng nào ...
prabhakaran S

102

Cái này làm việc tốt cho tôi:

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

Trong ES6, cú pháp đơn giản hơn một chút:

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

Như đã chỉ ra trong các ý kiến, phương pháp này có thể dẫn đến lỗi thời gian chạy trong một số trình duyệt khi ArrayBufferlớn. Giới hạn kích thước chính xác là phụ thuộc vào việc thực hiện trong mọi trường hợp.


38
Tôi thích phương pháp này tốt hơn cho sự đồng nhất, nhưng nhận được "lỗi kích thước ngăn xếp cuộc gọi tối đa vượt quá". Các kỹ thuật vòng lặp ở trên được xung quanh đó.
Jay

13
Tôi cũng nhận được một lỗi kích thước ngăn xếp, vì vậy tôi đã sử dụng câu trả lời của mobz và nó hoạt động rất tốt.
David Jones

12
Nó không hoạt động cho bộ đệm lớn. Sửa đổi nhẹ để làm cho nó hoạt động:btoa([].reduce.call(new Uint8Array(bufferArray),function(p,c){return p+String.fromCharCode(c)},''))
laggingreflex

1
Tôi đang thử tải lên tệp pdf 50mb bằng angualrjs và webapi2. Tôi đang sử dụng mã dòng trên, sau khi tải lên tệp, trang bị lỗi và bị treo. Bên dưới dòng mã, tôi đã được sử dụng nhưng nhận được giá trị null trong phương thức webapi. "var base64String = btoa (String.fromCharCode.apply (null, Uint8Array mới (mảngBuffer)));" vui lòng đề xuất bất kỳ ý tưởng nào ...
prabhakaran S

2
@Kugel btoaan toàn cho các ký tự trong phạm vi mã 0-255, vì đây là trường hợp (Hãy nghĩ về 8 in Uint8Array).
GOTO 0

42

Đối với những người thích nó ngắn, đây là một cái khác sử dụng Array.reducesẽ không gây ra lỗi tràn ngăn xếp:

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

4
Không chắc chắn nếu đó thực sự gợi cảm. Rốt cuộc, bạn đang tạo <amount of Bytes in the buffer>chuỗi mới.
Sơ khai

Thế còn btoa(new Uint8Array(arraybuffer).reduce((data,byte)=>(data.push(String.fromCharCode(byte)),data),[]).join(''))?
Roy Tinker

35

Có một cách không đồng bộ khác là sử dụng Blob và FileReader.

Tôi đã không kiểm tra hiệu suất. Nhưng đó là một cách nghĩ khác.

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"

Sử dụng dataurl.split(',', 2)[1]thay vì dataurl.substr(dataurl.indexOf(',')+1).
Carter Medlin

Điều này dường như không được đảm bảo để làm việc. Theo w3c.github.io/FileAPI/#su-f80bda5b readAsDataURL về mặt lý thuyết có thể trả về một phần trăm dữ liệu được mã hóaURI (Và có vẻ như đó thực sự là trường hợp trong jsdom )
TS

@CarterMedlin Tại sao sẽ splittốt hơn substring?
TS

chia ngắn hơn. nhưng dataurl có thể chứa một hoặc nhiều dấu phẩy (,), phân tách không an toàn.
cuixiping

12

Tôi đã sử dụng nó và làm việc cho tôi.

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

Không an toàn. Xem câu trả lời @chemoish
Kugel

11

Tôi đề nghị cho điều này là không nên sử dụng bản địa btoachiến lược-như họ không mã hóa một cách chính xác tất cả các ArrayBuffer's ...

viết lại các DOM atob () và btoa ()

Do DOMStrings là các chuỗi được mã hóa 16 bit, nên trong hầu hết các trình duyệt gọi window.btoa trên chuỗi Unicode sẽ gây ra ngoại lệ Ký tự ngoài phạm vi nếu một ký tự vượt quá phạm vi của ký tự được mã hóa ASCII 8 bit.

Mặc dù tôi chưa bao giờ gặp phải lỗi chính xác này, tôi đã phát hiện ra rằng nhiều lỗi ArrayBuffertôi đã cố mã hóa đã mã hóa không chính xác.

Tôi sẽ sử dụng khuyến nghị MDN hoặc ý chính.


btoakhông hoạt động trên String, nhưng OP đang hỏi ArrayBuffer.
tsh

1
Rất nhiều điều này, rất nhiều đoạn trích ở đây đề nghị điều sai! Tôi đã thấy lỗi này nhiều lần, trong đó mọi người mù quáng sử dụng atob và btoa.
Kugel

4
Tất cả các bộ đệm mảng phải được mã hóa tốt bằng cách sử dụng các chiến lược trong các câu trả lời khác, atob / btoa chỉ là vấn đề đối với văn bản chứa các ký tự lớn hơn 0xFF (mà mảng byte theo định nghĩa thì không). Cảnh báo MDN không áp dụng vì khi sử dụng chiến lược trong các câu trả lời khác, bạn được đảm bảo có một chuỗi chỉ bao gồm các ký tự ASCII vì mọi giá trị từ Uint8Array được đảm bảo nằm trong khoảng từ 0 đến 255, điều đó có nghĩa là String.fromCharCode được đảm bảo để trả về một nhân vật không nằm ngoài phạm vi.
Jamesernator

11
var blob = new Blob([arrayBuffer])

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

reader.readAsDataURL(blob);

1
Thêm một số lời giải thích cho câu trả lời của bạn xin vui lòng. Mã này có nghĩa là gì?
Oscar


1
đây là cách tiếp cận nhanh nhất - nhanh hơn hàng chục lần so với các phương pháp khác trong thử nghiệm giới hạn của tôi
jitin

tôi ước tôi đã tìm thấy giải pháp này như 8 giờ một lần nữa. ngày của tôi sẽ không bị lãng phí; (cảm ơn bạn
Kaiser Shahid

7

Dưới đây là 2 chức năng đơn giản để chuyển đổi Uint8Array thành Chuỗi Base64 và quay lại lần nữa

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

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

1
Đây là một câu trả lời khó hiểu. Trông nó không giống JavaScript hợp lệ và là Uint8Array một ArrayBuffer?
dùng1153660

@ user1153660 Thêm functiontừ khóa và nó sẽ hoạt động trong một trình duyệt hiện đại.
Yeti

Tuyệt vời! btoa (String.fromCharCode (... a)); là phiên bản ngắn nhất tôi đã thấy cho đến nay để mã hóa Uint8Array.
Nicolo

1
Điều này có vẻ tốt nhưng nếu mảng quá lớn, nó sẽ ném lỗi kích thước ngăn xếp cuộc gọi tối đa vượt quá.
Paweł Psztyć

0

Bạn có thể lấy được một mảng bình thường từ ArrayBufferbằng cách sử dụng Array.prototype.slice. Sử dụng một hàm như Array.prototype.mapđể chuyển đổi byte thành ký tự và joinchúng cùng nhau để tạo thành chuỗi.

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

}

Phương pháp này nhanh hơn vì không có chuỗi nối nào chạy trong nó.


Không an toàn. Xem câu trả lời @chemoish
Kugel

0

OP không chỉ định Môi trường chạy nhưng nếu bạn đang sử dụng Node.JS thì có một cách rất đơn giản để làm việc đó.

Accordig với các tài liệu chính thức của Node.JS https://nodejs.org/api/buffer.html#buffer_buffftimeand_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');

Ngoài ra, nếu bạn đang chạy trong Angular chẳng hạn, Lớp đệm cũng sẽ được cung cấp trong Môi trường trình duyệt.


Câu trả lời của bạn chỉ áp dụng cho NodeJS và sẽ không hoạt động trong trình duyệt.
jvatic

1
@jvatic Tôi thấy, OP không chỉ định rõ môi trường chạy, nên câu trả lời của tôi không sai, anh chỉ gắn thẻ Javascript. Vì vậy, tôi đã cập nhật câu trả lời của mình để làm cho nó ngắn gọn hơn. Tôi nghĩ rằng đây là một câu trả lời quan trọng bởi vì tôi đã tìm kiếm cách làm điều này và không thể có được câu trả lời tốt nhất cho vấn đề.
João Eduardo Soares e Silva

-3

Ở bên cạnh tôi, bằng cách sử dụng trình điều hướng Chrome, tôi đã phải sử dụng DataView () để đọc một mảngBuffer

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

Điều này tốt hơn, nếu bạn sử dụng JSZip để giải nén tệp lưu trữ từ chuỗi

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.