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


当前回答

假设你有一个arrayBuffer binaryStr:

let text = String.fromCharCode.apply(null, new Uint8Array(binaryStr));

然后你把文本赋值给状态。

其他回答

Yes:

const encstr = (`TextEncoder` in window) ? new TextEncoder().encode(str) : Uint8Array.from(str, c => c.codePointAt(0));

尽管Dennis和gengkev使用Blob/FileReader的解决方案有效,但我不建议采用这种方法。这是一种解决简单问题的异步方法,比直接解决方案要慢得多。我在html5rocks上发布了一个更简单(更快)的解决方案: http://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String

解决方案是:

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

编辑:

Encoding API帮助解决字符串转换问题。请查看Jeff Posnik在Html5Rocks.com上对上述原创文章的回复。

摘录:

Encoding API使原始字节和原生JavaScript字符串之间的转换变得简单,而不考虑需要使用的许多标准编码中的哪一种。

<pre id="results"></pre>

<script>
  if ('TextDecoder' in window) {
    // The local files to be fetched, mapped to the encoding that they're using.
    var filesToEncoding = {
      'utf8.bin': 'utf-8',
      'utf16le.bin': 'utf-16le',
      'macintosh.bin': 'macintosh'
    };

    Object.keys(filesToEncoding).forEach(function(file) {
      fetchAndDecode(file, filesToEncoding[file]);
    });
  } else {
    document.querySelector('#results').textContent = 'Your browser does not support the Encoding API.'
  }

  // Use XHR to fetch `file` and interpret its contents as being encoded with `encoding`.
  function fetchAndDecode(file, encoding) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', file);
    // Using 'arraybuffer' as the responseType ensures that the raw data is returned,
    // rather than letting XMLHttpRequest decode the data first.
    xhr.responseType = 'arraybuffer';
    xhr.onload = function() {
      if (this.status == 200) {
        // The decode() method takes a DataView as a parameter, which is a wrapper on top of the ArrayBuffer.
        var dataView = new DataView(this.response);
        // The TextDecoder interface is documented at http://encoding.spec.whatwg.org/#interface-textdecoder
        var decoder = new TextDecoder(encoding);
        var decodedString = decoder.decode(dataView);
        // Add the decoded file's text to the <pre> element on the page.
        document.querySelector('#results').textContent += decodedString + '\n';
      } else {
        console.error('Error while requesting', file, this);
      }
    };
    xhr.send();
  }
</script>

如果你在字符串中有二进制数据(从nodejs + readFile(…, 'binary'),或cypress + cy.fixture(…, 'binary'),等等),你不能使用TextEncoder。它只支持utf8。值为>= 128的字节被转换为2个字节。

ES2015:

a = Uint8Array.from(s, x => x.charCodeAt(0))

Uint8Array(33)[2、134、140、186、82、70、108、182、233、40、143、247、29、76、245、206、29、87、48、160、78、225、242、56、236、201、80、152、118、92、144、48]

s = String.fromCharCode.apply(null, a)

“ºRFl¶é(÷LõÎW0 Náò8ìÉPPv\0”

请看这里:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays/StringView (基于JavaScript ArrayBuffer接口的类似c语言的字符串接口)

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

我建议不要用

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。