Cách chuyển đổi ArrayBuffer thành và từ String

Renato Mangini

ArrayBuffers được dùng để truyền dữ liệu thô và một số API mới phụ thuộc vào các API này, bao gồm WebSockets, Web Intents 2](https://www.html5rock.com/en/tutorials/file/xhr2/) và WebWorkers. Tuy nhiên, vì chỉ mới xuất hiện trong thế giới JavaScript, nên đôi khi định dạng này bị hiểu sai hoặc sử dụng sai mục đích.

Về mặt ngữ nghĩa, ArrayBuffer chỉ đơn giản là một mảng các byte được xem thông qua một mặt nạ cụ thể. Mặt nạ này (một thực thể của ArrayBufferView) xác định cách căn chỉnh các byte cho phù hợp với cấu trúc dự kiến của nội dung. Ví dụ: nếu biết rằng các byte trong ArrayBuffer biểu thị một mảng số nguyên 16 bit chưa ký, bạn chỉ cần gói ArrayBuffer trong khung hiển thị Uint16Array là có thể điều khiển các phần tử của nó bằng cú pháp dấu ngoặc vuông như thể Uint16Array là một mảng số nguyên:

// suppose buf contains the bytes [0x02, 0x01, 0x03, 0x07]
// notice the multibyte values respect the hardware endianess, which is little-endian in x86
var bufView = new Uint16Array(buf);
if (bufView[0]===258) {   // 258 === 0x0102
    console.log("ok");
}
bufView[0] = 255;    // buf now contains the bytes [0xFF, 0x00, 0x03, 0x07]
bufView[0] = 0xff05; // buf now contains the bytes [0x05, 0xFF, 0x03, 0x07]
bufView[1] = 0x0210; // buf now contains the bytes [0x05, 0xFF, 0x10, 0x02]

Một câu hỏi thực tế phổ biến về ArrayBuffer là cách chuyển đổi String thành ArrayBuffer và ngược lại. Vì thực tế, ArrayBuffer là một mảng byte, nên việc chuyển đổi này yêu cầu cả hai đầu phải thống nhất về cách biểu thị các ký tự trong Chuỗi dưới dạng byte. Bạn có thể đã thấy "thoả thuận" này trước đây: đó là phương thức mã hoá ký tự của Chuỗi (và "điều khoản thoả thuận" thông thường là Unicode UTF-16 và iso8859-1). Do đó, giả sử bạn và bên kia đã thống nhất về bộ mã hoá UTF-16, mã chuyển đổi có thể có dạng như sau:

function ab2str(buf) {
    return String.fromCharCode.apply(null, new Uint16Array(buf));
}
function str2ab(str) {
    var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
    var bufView = new Uint16Array(buf);
    for (var i=0, strLen=str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
    }
    return buf;
}

Bạn có thể lưu ý cách sử dụng Uint16Array. Đây là khung hiển thị ArrayBuffers giúp căn chỉnh các byte của ArrayBuffers dưới dạng phần tử 16 bit. Lớp này không tự xử lý việc mã hoá ký tự (được String.fromCharCodestr.charCodeAt xử lý dưới dạng Unicode).

Có một câu hỏi phổ biến về StackOverflow về vấn đề này, có câu trả lời được đánh giá cao bằng một giải pháp hơi phức tạp đối với lượt chuyển đổi: tạo FileReader để hoạt động như trình chuyển đổi và cung cấp Blob có chứa Chuỗi vào. Mặc dù phương thức này hoạt động, nhưng nó khó đọc và tôi cho rằng phương thức này chậm. Vì những nghi ngờ vô căn cứ đã gây ra nhiều sai lầm trong lịch sử nhân loại, nên chúng ta hãy áp dụng một phương pháp tiếp cận khoa học hơn ở đây. Tôi đã xem jsperf hai phương thức và kết quả xác nhận sự nghi ngờ của tôi, bạn hãy xem bản minh hoạ tại đây.

Trong Chrome 20, việc sử dụng mã thao tác ArrayBuffer trực tiếp trên bài viết này nhanh hơn gần 27 lần so với khi sử dụng phương thức FileReader/Blob.