是否有一种普遍接受的技术可以有效地将JavaScript字符串转换为arraybuffer,反之亦然?具体来说,我希望能够将ArrayBuffer的内容写入localStorage,然后再将其读回来。


当前回答

以下所有内容都是关于从数组缓冲区中获取二进制字符串

我建议不要用

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

因为它

大缓冲区崩溃(有人写了关于246300的“神奇”大小,但我得到的最大调用堆栈大小超过了120000字节缓冲区的错误(Chrome 29)) 它的性能真的很差(见下文)

如果您确实需要同步解决方案,请使用类似

var
  binaryString = '',
  bytes = new Uint8Array(arrayBuffer),
  length = bytes.length;
for (var i = 0; i < length; i++) {
  binaryString += String.fromCharCode(bytes[i]);
}

它和前一个一样慢,但工作正常。在写这篇文章的时候,似乎还没有针对这个问题的快速同步解决方案(本主题中提到的所有库都使用相同的方法来实现它们的同步特性)。

但我真正推荐的是使用Blob + FileReader方法

function readBinaryStringFromArrayBuffer (arrayBuffer, onSuccess, onFail) {
  var reader = new FileReader();
  reader.onload = function (event) {
    onSuccess(event.target.result);
  };
  reader.onerror = function (event) {
    onFail(event.target.error);
  };
  reader.readAsBinaryString(new Blob([ arrayBuffer ],
    { type: 'application/octet-stream' }));
}

唯一的缺点(并非所有缺点)是它是异步的。它比以前的解决方案快8-10倍!(一些细节:在我的环境中,同步解决方案需要950-1050 ms才能获得2.4Mb的缓冲区,而使用FileReader的解决方案需要大约100-120 ms才能获得相同数量的数据。我已经在100Kb缓冲区上测试了这两种同步解决方案,它们几乎花费了相同的时间,所以使用'apply'的循环并不会慢很多。)

BTW在这里:如何转换ArrayBuffer和字符串作者比较两种方法像我和得到完全相反的结果(他的测试代码在这里)为什么这么不同的结果?可能是因为他的测试字符串有1Kb长(他称之为“veryLongStr”)。我的缓冲区是一张非常大的JPEG图像,大小为2.4Mb。

其他回答

你可以使用Encoding标准中的TextEncoder和TextDecoder,该标准由stringencoding库填充,用于将字符串转换为ArrayBuffers:

var uint8array = new TextEncoder().encode(string);
var string = new TextDecoder(encoding).decode(uint8array);
var decoder = new TextDecoder ();
var string = decoder.decode (arrayBuffer);

参见https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/decode

使用splat unpacking代替loops:

arrbuf =新uint8阵列([104、101、108、108、111]) text = String.fromCharCode(.. arrbuf) console . log(文本)

对于子字符串,可以使用arrbuf.slice()。

下面是一个Typescript的工作实现:

bufferToString(buffer: ArrayBuffer): string {
    return String.fromCharCode.apply(null, Array.from(new Uint16Array(buffer)));
}

stringToBuffer(value: string): ArrayBuffer {
    let buffer = new ArrayBuffer(value.length * 2); // 2 bytes per char
    let view = new Uint16Array(buffer);
    for (let i = 0, length = value.length; i < length; i++) {
        view[i] = value.charCodeAt(i);
    }
    return buffer;
}

在使用crypt .subtle时,我已经使用它进行了许多操作。

  stringToArrayBuffer(byteString) {
    var byteArray = new Uint8Array(byteString.length);
    for (var i = 0; i < byteString.length; i++) {
      byteArray[i] = byteString.codePointAt(i);
    }
    return byteArray;
  }
  arrayBufferToString(buffer) {
    var byteArray = new Uint8Array(buffer);
    var byteString = '';
    for (var i = 0; i < byteArray.byteLength; i++) {
      byteString += String.fromCodePoint(byteArray[i]);
    }
    return byteString;
  }