数据的流式获取

阅读数:4 评论数:0

跳转到新版页面

分类

html/css/js

正文

一、概述

在JavaScript中,流式获取数据通常是指以流的形式逐步接收数据,而不是一次性接收全部数据。这在处理大量数据或者需要边下载边处理数据时特别有用。在现代浏览器中,Fetch API 提供了一个接口来以流的形式读取内容。

以下是使用 Fetch API 进行流式读取数据的基本示例:

fetch('some-url')
  .then(response => {
    const reader = response.body.getReader();
    return new ReadableStream({
      start(controller) {
        function push() {
          // "done"是一个布尔值,表示流是否已经结束
          // "value"是一个Uint8Array,包含从流中读取的数据
          reader.read().then(({ done, value }) => {
            if (done) {
              // 当没有更多数据需要读取时,关闭流
              controller.close();
              return;
            }
            // 将数据块(chunk)放入流中
            controller.enqueue(value);
            push();
          }).catch(err => {
            // 处理流读取过程中的错误
            console.error('stream reading error', err);
            controller.error(err);
          });
        }
        
        push();
      }
    });
  })
  .then(stream => {
    // 新的 Response 对象可以使用刚才创建的流
    return new Response(stream);
  })
  .then(response => {
    // 从流中获取文本,或者使用其他方法处理流(如 response.blob())
    return response.text();
  })
  .then(text => {
    console.log(text);
  })
  .catch(err => {
    console.error('fetch error', err);
  });

在上面的代码中:

  1. 使用 fetch 发起网络请求。
  2. response.body.getReader() 返回一个 ReadableStream 的阅读器(reader),它允许你逐块(chunk)读取数据流。
  3. 使用 read() 方法来读取流中的数据。每次调用 read() 都会返回一个 Promise,该 Promise 解析为一个对象,其中包含两个属性:donevaluedone 是一个布尔值,当流结束时为 truevalue 是一个 Uint8Array,包含从流中读取的数据块。
  4. donetrue 时,表示流结束,我们调用 controller.close() 来关闭流。
  5. 当还有数据时,我们通过 controller.enqueue(value) 将数据块放入流,并递归调用 push 函数继续读取下一个数据块。
  6. 最后,我们可以将 ReadableStream 转换为 Response 对象,并使用 text()json()blob() 方法来处理数据。

二、TextDecoder

在JavaScript中使用TextDecoder与流式获取数据相结合是为了逐步将接收到的二进制数据转换为文本。这在处理大型文本文件或数据流时特别有用,因为你可以在整个内容下载完成之前开始处理文本。

TextDecoder接口允许你将一个二进制数据(通常是Uint8Array)转换为字符串。结合Fetch API的流式读取功能,你可以实现流式文本解码。

以下是一个示例,演示了如何使用TextDecoder流式地获取和解码文本数据:

// 创建一个新的TextDecoder实例,默认是UTF-8编码
const decoder = new TextDecoder('utf-8');
let result = '';

fetch('some-url')
  .then(response => {
    const reader = response.body.getReader();

    // 读取流
    function read() {
      reader.read().then(({ done, value }) => {
        if (done) {
          // 当流完成时,输出最终的文本
          console.log('Stream complete', result);
          return;
        }

        // 使用TextDecoder将Uint8Array转换为字符串
        const str = decoder.decode(value, { stream: true });
        result += str;

        // 控制台输出流式解码的结果片段
        console.log(str);

        // 继续读取下一部分
        read();
      }).catch(error => {
        console.error('Error while reading from the stream', error);
      });
    }

    read();
  })
  .catch(error => {
    console.error('Fetch error', error);
  });

使用TextDecoder{stream: true}选项是非常重要的,因为它允许解码器处理可能跨多个数据块的多字节字符。如果不设置这个选项,解码器在每次调用decode方法时都会认为它是独立的数据块,这可能会导致字符断裂。




相关推荐