从ReadableStream对象中检索数据?

noo*_*oob 98 javascript fetch node.js reactjs

如何从ReadableStream对象获取信息?

我正在使用Fetch API,我没有从文档中看到这一点.

正文作为ReadableStream返回,我只想访问此流中的属性.在浏览器开发工具的响应下,我似乎将这些信息以Javascript对象的形式组织到属性中.

fetch('http://192.168.5.6:2000/api/car', obj)
    .then((res) => {
        if(res.status == 200) {
            console.log("Success :" + res.statusText);   //works just fine
        }
        else if(res.status == 400) {
            console.log(JSON.stringify(res.body.json());  //res.body is undefined.
        }

        return res.json();
    })
Run Code Online (Sandbox Code Playgroud)

提前致谢.

Ash*_*son 117

要从ReadableStream您需要调用其中一种转换方法(此处提供的文档)中访问数据.

举个例子:

fetch('https://jsonplaceholder.typicode.com/posts/1')
  .then(function(response) {
    // The response is a Response instance.
    // You parse the data into a useable format using `.json()`
    return response.json();
  }).then(function(data) {
    // `data` is the parsed version of the JSON returned from the above endpoint.
    console.log(data);  // { "userId": 1, "id": 1, "title": "...", "body": "..." }
  });
Run Code Online (Sandbox Code Playgroud)

希望这有助于澄清事情.


Noe*_*oel 33

有些人可能会发现一个async有用的例子:

var response = await fetch("https://httpbin.org/ip");
var body = await response.json(); // .json() is asynchronous and therefore must be awaited
Run Code Online (Sandbox Code Playgroud)

json()将响应的主体从a ReadableStream转换为json对象.

await语句必须被包裹在一个async功能,但是可以运行await在Chrome的控制台直接语句(如版本62).


cda*_*uth 31

您可能提出了错误的问题来解决您的问题,但这是您实际问题的答案。stream/consumersNode.js模块的源代码可能是一个灵感。

res.body是 a ,它以sReadableStream的形式发出块。请注意,在其他地方创建的对象可能会发出除 之外的其他数据类型,并且在这些情况下需要调整本答案中概述的方法。Uint8ArrayReadableStreamUint8Array

有多种方法可以使用这样的流:

将流转换为Uint8Array

使用new Response(stream).arrayBuffer()

如果您想一次性检索流的全部内容,最简单的方法是将其包装在一个Response对象中。然后,您可以使用多种方法之一以字符串、JSON 对象、数组缓冲区或其他形式检索对象。例如,要将其作为数组缓冲区检索:

export async function streamToArrayBuffer(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> {
   return new Uint8Array(await new Response(stream).arrayBuffer());
}
Run Code Online (Sandbox Code Playgroud)

请注意,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);
}
Run Code Online (Sandbox Code Playgroud)

使用异步迭代器

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);
}
Run Code Online (Sandbox Code Playgroud)

将来当浏览器支持时Array.fromAsync(),这可以缩短为:

export async function streamToArrayBuffer(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> {
    return concatArrayBuffers(await Array.fromAsync(stream));
}
Run Code Online (Sandbox Code Playgroud)

将流转换为string

使用new Response(stream).text()

就像上面描述的数组缓冲区一样,可以使用以下方法将流Uint8Array转换为字符串Response.text()

export async function streamToString(stream: ReadableStream<Uint8Array>): Promise<string> {
   return await new Response(stream).text();
}
Run Code Online (Sandbox Code Playgroud)

使用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;
}
Run Code Online (Sandbox Code Playgroud)

在支持它的浏览器中,您还可以使用异步迭代器协议使用此字符串流:

export async function streamToText(stream: ReadableStream<Uint8Array>): Promise<string> {
    let result = '';
    for (const chunk of stream.pipeThrough(new TextDecoderStream()).getReader())
        result += chunk;
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

或者在支持 的浏览器中Array.fromAsync(),甚至更短:

export async function streamToText(stream: ReadableStream<Uint8Array>): Promise<string> {
    const chunks = await Array.fromAsync(stream.pipeThrough(new TextDecoderStream()).getReader()));
    return chunks.join("");
}
Run Code Online (Sandbox Code Playgroud)

使用TextDecoder

要将上述某些函数生成的 s 转换Uint8Array为字符串,您可以使用TextDecoder

const buffer = await streamToArrayBuffer(res.body);
const text = new TextDecoder().decode(buffer);
Run Code Online (Sandbox Code Playgroud)

请注意,这只能用于整个内容,而不是单个UInt8Array块,因为某些字符可能由多个字节组成,并且可能在块之间分割。

将流转换为 JSON 对象

使用new Response(stream).json()

Uint8Array就像上面描述的数组缓冲区和字符串一样,可以使用以下方法将流解析为 JSON Response.json()

export async function streamToJson(stream: ReadableStream<Uint8Array>): Promise<unknown> {
   return await new Response(stream).json();
}
Run Code Online (Sandbox Code Playgroud)

使用JSON.parse()

使用上述任何方法将流转换为字符串,然后用于JSON.parse()解析该字符串。

  • 对于那些拥有“ReadableStream”并且只想要一个简单/简单的单行代码来从中获取文本的人,一个简短的技巧是将其包装在一个新的“Response”(或“Request”)对象中,然后使用`text` 方法:`等待新的 Response(request.body).text()`。[此处](/sf/answers/5196607461/) 发布了答案。 (5认同)

joe*_*joe 29

对于那些有 aReadableStream并且想要从中获取文本的人,一个简短的技巧是将其包装在 new Response(或Request) 中,然后使用以下text方法:

let text = await new Response(yourReadableStream).text();
Run Code Online (Sandbox Code Playgroud)


pin*_*yid 23

res.json()返回一个承诺.试试......

res.json().then(body => console.log(body));
Run Code Online (Sandbox Code Playgroud)

  • 我试过这个,它打印出Promise而不是身体. (3认同)
  • 尝试链接“.then”调用:“fetch(...).then(res =&gt; res.json()).then(data =&gt; console.log(data))” (2认同)

Dan*_*ist 11

派对有点晚了,但是从使用Sharepoint Framework的Odata $批量请求生成的ReadableStream中获得一些有用的东西有一些问题.

和OP有类似的问题,但我的解决方案是使用与.json()不同的转换方法.在我的情况下.text()就像一个魅力.然而,为了从文本文件中获取一些有用的JSON,必须进行一些调整.

  • 谢谢!这对我有用。我正在从我的 Laravel 服务器发送一个 Illuminate http 响应,并带有一个简单的 `return $data;`。我终于能够在浏览器中读取这个响应了`fetch(...).then(response =&gt; response.text()).then(data =&gt; console.log(data));` (2认同)

Chr*_*row 10

请注意,您只能读取一次流,因此在某些情况下,您可能需要克隆响应以重复读取它:

fetch('example.json')
  .then(res=>res.clone().json())
  .then( json => console.log(json))

fetch('url_that_returns_text')
  .then(res=>res.clone().text())
  .then( text => console.log(text))
Run Code Online (Sandbox Code Playgroud)


Ale*_*fee 8

如果您只想将响应作为文本而不希望将其转换为JSON,请使用https://developer.mozilla.org/en-US/docs/Web/API/Body/text,然后使用then它来获取实际的承诺的结果:

fetch('city-market.md')
  .then(function(response) {
    response.text().then((s) => console.log(s));
  });
Run Code Online (Sandbox Code Playgroud)

要么

fetch('city-market.md')
  .then(function(response) {
    return response.text();
  })
  .then(function(myText) {
    console.log(myText);
  });
Run Code Online (Sandbox Code Playgroud)