我正在使用流的获取API获取大量数据,但我只想处理其中的前15MB。
我正在从获取数据创建一个CSV文件,但是当我写入文件时,该文件似乎处于无效状态,行似乎乱码。
也许是因为我没有正确处理块边界。
下面是我的代码。
const stream = await fetch(fetchUrl);
let receivedLength = 0;
let reader = stream.body.getReader();
const decoder = new TextDecoder('utf-16le');
while(true) {
const {done, value} = await reader.read();
if (done || receivedLength >= 15000000) {
break;
}
receivedLength += value.length;
let v = decoder.decode(value, {stream: true});
}
如何仅处理从提取 API 返回的部分数据并确保保留边界。谢谢
免责声明:这个答案假设你的目标是非常现代的浏览器,支持TransformStreams
和TextDecoderStream
(这是TransformStream的一个子类(以及解码你的输入数据正在使用的UTF-16LE。
首先,请记住您正在读取二进制流(这意味着基本单位是字节(。当您读取流时,浏览器会在流块可用时为您提供它们(只要您已经使用了前一个块(。如何将流划分为块是非常随意的(例如,您可能会获得 64kb 的块,或者在滞后连接上,每个 TCP 数据包中有多少(。
在您有机会解析 CSV 行失败之前,您正在读取 UTF-16,因此您可能会在解析字符时失败。这是因为 UTF-16 将每个字符编码为两个或多个字节,并且输入流的某些块可能会在字符中间结束。
您可以通过将{stream:true}
作为选项传递给TextDecoder.decode
并将未读字节携带到下一个块来避免该错误。但是有一个更好的方法。
不是在原始流上获取读取器,而是通过 TextDecoderStream 通过管道传输它:
const decoder = new TextDecoderStream('utf-16le')
const characterStream = stream.body.pipeThrough(decoder)
现在,当你在characterStream
上得到一个读者时,你不必担心字符。下一部分涉及 CSV 解析。显然,如果流可以在字符中间分割,则可以在 CSV 行中间分割。与以前一样,您需要一个从字符流读取并输出行的 TransformStream,并且该流的内部应该负责将未完成的行转移到下一个块。
没有像文本解码那样的本机解决方案,但是有现有的实现,我会让你寻找它们并选择一个。
获得行流后,您可以通过CSV解析器或获取阅读器。无论哪种方式,使用转换流,您都无需担心数据分块方式引起的错误。