Chrome中ReadableStreamBYOBReader.read忽略偏移量问题
Chrome中ReadableStreamBYOBReader忽略偏移量写入缓冲区的问题
问题概述
使用ReadableStreamBYOBReader实现零拷贝文件读取时,Chrome浏览器存在异常行为:调用read()方法传入带偏移量的Uint8Array视图时,阅读器未按指定偏移写入数据,而是覆盖了缓冲区开头的内容。该问题在MDN官方示例中同样可复现。
复现代码
<input type="file" onchange="upload(event)" /> <script> async function upload(event) { const reader = event.target.files[0].stream().getReader({ mode: 'byob' }); let buffer = new ArrayBuffer(16777216); let offset = 0; const read1 = await reader.read(new Uint8Array(buffer, offset, 8388608)); console.log('1', read1.value, new Uint8Array(read1.value.buffer)); buffer = read1.value.buffer; offset += read1.value.byteLength; const read2 = await reader.read(new Uint8Array(buffer, offset, 8388608)); console.log('2', read2.value, new Uint8Array(read2.value.buffer)); } </script>
预期行为
- 每次
read()返回的readX.value应为初始16MB缓冲区的偏移视图 - 第一次读取后,缓冲区仅包含第一次读取的数据
- 第二次读取后,第二次的数据应写入缓冲区的指定偏移位置,缓冲区包含两次读取的完整数据
浏览器表现对比
Firefox(符合预期)
1 Uint8Array(65536) [ 0, 0, 0, 32, 102, … ] Uint8Array(16777216) [ 0, 0, 0, 32, 102, … ] 2 Uint8Array(65536) [ 100, 63, 56, 65, 149, … ] Uint8Array(16777216) [ 0, 0, 0, 32, 102, … ]
Chrome(异常行为)
1 Uint8Array(65536) [ 0, 0, 0, 32, 102, …] Uint8Array(16777216) [ 0, 0, 0, 32, 102, …] 2 Uint8Array(524288) [ 28, 196, 91, 173, 156, …] Uint8Array(16777216) [ 100, 63, 56, 65, 149, …]
问题细节
测试文件关键内容的位置:
[ 0, 0, 0, 32, 102]:文件起始位置[100, 63, 56, 65, 149]:文件偏移65536字节处[ 28, 192, 91, 173, 156]:文件偏移131072字节处
Chrome第二次读取时的异常:
- 返回的
read2.value包含了文件偏移131072字节处的内容 - 本该写入缓冲区偏移65536字节处的文件数据(
[100, 63, 56, 65, 149])被错误写入缓冲区开头,完全忽略了传入的偏移量参数
注意:Chrome的读取块大小不固定(有时会读取几MB数据),仅当使用较大的缓冲区(如8MB)时才会触发该问题,小缓冲区场景下无法复现。
临时规避方案
目前可通过手动复制数据到目标偏移位置的方式绕过该问题,但会失去零拷贝的性能优势:
async function upload(event) { const reader = event.target.files[0].stream().getReader({ mode: 'byob' }); let buffer = new ArrayBuffer(16777216); let offset = 0; // 第一次读取并手动复制 let readResult = await reader.read(new Uint8Array(8388608)); new Uint8Array(buffer, offset).set(readResult.value); offset += readResult.value.byteLength; // 第二次读取并手动复制 readResult = await reader.read(new Uint8Array(8388608)); new Uint8Array(buffer, offset).set(readResult.value); offset += readResult.value.byteLength; console.log('最终完整缓冲区', new Uint8Array(buffer)); }
内容的提问来源于stack exchange,提问作者Sinder
相关产品推荐
相关产品推荐

