是否有一种普遍接受的技术可以有效地将JavaScript字符串转换为arraybuffer,反之亦然?具体来说,我希望能够将ArrayBuffer的内容写入localStorage,然后再将其读回来。
当前回答
Just
const buffer = thisReturnsBuffers();
const blob = new Blob([buffer], {type: 'text/plain; charset=utf-8'});
blob.text().then(text => console.log(text));
Or
const stringVal = "string here";
const blob = new Blob([stringVal], {type: 'text/plain; charset=utf-8'});
blob.arrayBuffer().then(buffer => console.log(buffer));
你们为什么要把事情搞得这么复杂?
其他回答
(更新请参阅这个答案的后半部分,我(希望)提供了一个更完整的解决方案。)
我也遇到了这个问题,以下是我在FF 6中的工作(一个方向):
var buf = new ArrayBuffer( 10 );
var view = new Uint8Array( buf );
view[ 3 ] = 4;
alert(Array.prototype.slice.call(view).join(""));
当然,不幸的是,您最终得到的是数组中值的ASCII文本表示,而不是字符。尽管如此,它仍然(应该)比循环更有效。 如。对于上面的例子,结果是0004000000,而不是几个空字符&一个chr(4)。
编辑:
看完这里的MDC,你可以从一个数组创建一个ArrayBuffer,如下所示:
var arr = new Array(23);
// New Uint8Array() converts the Array elements
// to Uint8s & creates a new ArrayBuffer
// to store them in & a corresponding view.
// To get at the generated ArrayBuffer,
// you can then access it as below, with the .buffer property
var buf = new Uint8Array( arr ).buffer;
为了回答你最初的问题,这允许你像下面这样转换ArrayBuffer <-> String:
var buf, view, str;
buf = new ArrayBuffer( 256 );
view = new Uint8Array( buf );
view[ 0 ] = 7; // Some dummy values
view[ 2 ] = 4;
// ...
// 1. Buffer -> String (as byte array "list")
str = bufferToString(buf);
alert(str); // Alerts "7,0,4,..."
// 1. String (as byte array) -> Buffer
buf = stringToBuffer(str);
alert(new Uint8Array( buf )[ 2 ]); // Alerts "4"
// Converts any ArrayBuffer to a string
// (a comma-separated list of ASCII ordinals,
// NOT a string of characters from the ordinals
// in the buffer elements)
function bufferToString( buf ) {
var view = new Uint8Array( buf );
return Array.prototype.join.call(view, ",");
}
// Converts a comma-separated ASCII ordinal string list
// back to an ArrayBuffer (see note for bufferToString())
function stringToBuffer( str ) {
var arr = str.split(",")
, view = new Uint8Array( arr );
return view.buffer;
}
为了方便起见,这里有一个将原始Unicode字符串转换为ArrayBuffer的函数(只适用于ASCII/单字节字符)
function rawStringToBuffer( str ) {
var idx, len = str.length, arr = new Array( len );
for ( idx = 0 ; idx < len ; ++idx ) {
arr[ idx ] = str.charCodeAt(idx) & 0xFF;
}
// You may create an ArrayBuffer from a standard array (of values) as follows:
return new Uint8Array( arr ).buffer;
}
// Alerts "97"
alert(new Uint8Array( rawStringToBuffer("abc") )[ 0 ]);
上面允许你从ArrayBuffer -> String返回到ArrayBuffer,在那里字符串可能存储在eg. . localstorage:)
希望这能有所帮助,
Dan
在使用了mangini的从ArrayBuffer转换到String的解决方案- ab2str(这是我发现的最优雅和有用的一个-谢谢!)之后,我在处理大型数组时遇到了一些问题。更具体地说,调用String.fromCharCode.apply(null, new Uint16Array(buf));抛出错误:
传递给Function.prototype.apply的参数数组太大。
为了解决它(绕过),我决定处理输入ArrayBuffer块。所以修改后的解是:
function ab2str(buf) {
var str = "";
var ab = new Uint16Array(buf);
var abLen = ab.length;
var CHUNK_SIZE = Math.pow(2, 16);
var offset, len, subab;
for (offset = 0; offset < abLen; offset += CHUNK_SIZE) {
len = Math.min(CHUNK_SIZE, abLen-offset);
subab = ab.subarray(offset, offset+len);
str += String.fromCharCode.apply(null, subab);
}
return str;
}
块大小设置为2^16,因为这是我发现在我的开发环境中工作的大小。设置一个更高的值会导致同样的错误再次发生。可以通过将CHUNK_SIZE变量设置为不同的值来更改它。偶数是很重要的。
性能注意事项—我没有对此解决方案进行任何性能测试。但是,由于它基于前面的解决方案,并且可以处理大型数组,所以我认为没有理由不使用它。
以下所有内容都是关于从数组缓冲区中获取二进制字符串
我建议不要用
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。
atob()返回的“本机”二进制字符串是一个每个字符1字节的数组。
所以我们不应该在一个字符中存储2个字节。
var arrayBufferToString = function(buffer) {
return String.fromCharCode.apply(null, new Uint8Array(buffer));
}
var stringToArrayBuffer = function(str) {
return (new Uint8Array([].map.call(str,function(x){return x.charCodeAt(0)}))).buffer;
}
请看这里:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays/StringView (基于JavaScript ArrayBuffer接口的类似c语言的字符串接口)