你可能问错了问题来解决你的问题,但这里是对你实际问题的答案。一个灵感可能是Node.js的源代码
stream/consumers
模块。
res.body
是一个
ReadableStream
,它以
Uint8Array
的形式发出数据块。请注意,其他地方创建的
ReadableStream
对象可能发出的数据类型不同于
Uint8Array
,在这些情况下,需要调整本答案中概述的方法。
有多种方法可以消费这样的流:
将流转换为Uint8Array
使用new Response(stream).arrayBuffer()
如果你想一次性获取流的全部内容,最简单的方法是将其包装在一个
Response
对象中。然后,你可以使用其中的一种
方法 将对象作为字符串、JSON 对象、数组缓冲区或其他形式进行获取。例如,要将其作为数组缓冲区进行获取:
export async function streamToArrayBuffer(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> {
return new Uint8Array(await new Response(stream).arrayBuffer());
}
请注意,
new Response(stream)
只适用于
Uint8Array
流。如果流发出任何其他类型(例如字符串),将导致
TypeError: Received non-Uint8Array chunk
错误。
使用 stream.getReader()
下面的函数将收集所有的块到一个单独的 Uint8Array
中:
function concatArrayBuffers(chunks: Uint8Array[]): Uint8Array) {
const result = new Uint8Array(chunks.reduce((a, c) => a + c.length, 0);
let offset = 0;
for (const chunk of chunks) {
result.set(chunk, offset);
offset += chunk.length;
}
return result;
}
export async function streamToArrayBuffer(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> {
const chunks: Uint8Array[] = [];
const reader = stream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
} else {
chunks.push(value);
}
}
return concatArrayBuffers(chunks);
}
使用异步迭代器
ReadableStream
实现了 异步迭代器协议。然而,大多数浏览器尚不支持此功能,但在 Node.js 中,您已经可以使用它(如果使用 TypeScript,您需要使用 NodeJS.ReadableStream
接口,参见 此讨论)。
以下代码将收集所有的块并存储在一个单独的 Uint8Array
中:
export async function streamToArrayBuffer(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> {
const chunks: Uint8Array[] = [];
for await (const chunk of stream) {
chunks.push(chunk);
}
return concatArrayBuffers(chunks);
}
将来当浏览器支持
Array.fromAsync()
时,可以简化为:
export async function streamToArrayBuffer(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> {
return concatArrayBuffers(await Array.fromAsync(stream));
}
将流转换为字符串
使用new Response(stream).text()
就像上面描述的数组缓冲区一样,可以使用Response.text()
将Uint8Array
的流转换为字符串:
export async function streamToString(stream: ReadableStream<Uint8Array>): Promise<string> {
return await new Response(stream).text();
}
使用TextDecoderStream
TextDecoderStream
会将UInt8Array
块的流转换为string
块的流。这样,您可以直接将流的内容收集为字符串。请注意,Firefox浏览器的支持仅在2022年9月才添加,因此您可能还不想在生产环境中使用此功能。
export async function streamToText(stream: ReadableStream<Uint8Array>): Promise<string> {
let result = '';
const reader = stream.pipeThrough(new TextDecoderStream()).getReader();
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
result += value;
}
return result;
}
在支持的浏览器中,您还可以使用异步迭代器协议来消耗这个字符串流。
export async function streamToText(stream: ReadableStream<Uint8Array>): Promise<string> {
let result = '';
for (const chunk of stream.pipeThrough(new TextDecoderStream()).getReader())
result += chunk;
}
return result;
}
在支持
Array.fromAsync()
的浏览器中,甚至更简短:
export async function streamToText(stream: ReadableStream<Uint8Array>): Promise<string> {
const chunks = await Array.fromAsync(stream.pipeThrough(new TextDecoderStream()).getReader()));
return chunks.join("");
}
使用TextDecoder
要将上述某些函数生成的Uint8Array
转换为字符串,您可以使用TextDecoder
:
const buffer = await streamToArrayBuffer(res.body);
const text = new TextDecoder().decode(buffer);
请注意,这仅适用于整个内容,而不是单个
UInt8Array
块,因为某些字符可能由多个字节组成,并可能在块之间分割。
将流转换为JSON对象
使用new Response(stream).json()
就像上面描述的数组缓冲区和字符串一样,可以使用Response.json()
将Uint8Array
流解析为JSON:
export async function streamToJson(stream: ReadableStream<Uint8Array>): Promise<unknown> {
return await new Response(stream).json();
}
使用 JSON.parse()
使用上述任何方法将流转换为字符串,然后使用 JSON.parse()
解析该字符串。
response.Body.json()
, 但是出现了 italic TypeError: Cannot read property 'json' of undefined italic 的错误。 这是因为 bodyUsed 属性也被设置为 false 吗?然而,我可以在浏览器开发工具的响应选项卡下查看此正文内容。我想检索出其中的错误消息。 - noobconsole.log(res.json());
会发生什么?你能看到你期望的数据吗? - Ashley 'CptLemming' Wilsonres.status == 200
,你是不是试图将响应作为流读取? - guest271314