获取 API:仅处理部分区块数据



我正在使用流的获取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 返回的部分数据并确保保留边界。谢谢

免责声明:这个答案假设你的目标是非常现代的浏览器,支持TransformStreamsTextDecoderStream(这是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解析器或获取阅读器。无论哪种方式,使用转换流,您都无需担心数据分块方式引起的错误。

最新更新