likes
comments
collection
share

实现分块加载大文件

作者站长头像
站长
· 阅读数 20

在本文中,我们将探讨如何使用 React 前端框架和 Koa2 后端框架实现分块加载大文件。这种方法可以提高页面加载速度和性能,尤其适用于加载较大的文章或其他内容。

如:有一份100MB的数据,我们可以采用分块加载的方式,无需等待请求彻底走完再渲染加载。

后端:使用 Koa2 实现分块传输

首先,我们需要设置后端以支持分块传输编码。以下是使用 Koa2 的示例代码:

const Koa = require("koa");
const fs = require("fs");
const app = new Koa();

app.use(async (ctx) => {
  const filePath = "./large-article.txt"; // 你的大文件路径
  const CHUNK_SIZE = 10000; // 设定每个分块的大小(例如,1 万字)
  const fileSize = fs.statSync(filePath).size;

  // 设置响应头以支持分块传输编码
  ctx.set("Content-Type", "text/plain");
  ctx.set("Transfer-Encoding", "chunked");
  ctx.set("Content-Length", fileSize);

  // 通过 Koa 的 Readable Stream 逐个发送分块
  ctx.body = fs.createReadStream(filePath, { highWaterMark: CHUNK_SIZE });
});

app.listen(3000, () => {
  console.log("Server is running on port 3000");
});

在这个示例中,我们首先设置 Koa2 以支持分块传输编码。然后,我们使用 Node.js 的 fs 模块创建一个可读流,将大文件分成每个 CHUNK_SIZE 大小的块,并将其作为 Koa 的响应体。

前端:使用 React 实现分块加载

接下来,我们需要在 React 应用中实现分块加载。以下是一个简单的 React 组件示例:

import React, { useEffect, useState } from "react";

const CHUNK_SIZE = 10000; // 1万字
const articleUrl = "http://localhost:3000"; // 替换为你的后端地址

function LargeArticle() {
  const [articleContent, setArticleContent] = useState("");

  useEffect(() => {
    fetchArticleInChunks();
  }, []);

  async function fetchArticleInChunks() {
    const response = await fetch(articleUrl);

    if (!response.body) {
      throw new Error("ReadableStream not yet supported in this browser.");
    }

    const reader = response.body.getReader();
    let result = "";
    let receivedLength = 0;

    while (true) {
      const { done, value } = await reader.read();

      if (done) {
        break;
      }

      receivedLength += value.length;
      const chunk = new TextDecoder("utf-8").decode(value);

      // 处理新接收到的文章部分
      processChunk(chunk);

      // 更新进度
      console.log(`Received ${receivedLength} of ${response.headers.get("Content-Length")} bytes`);

      // 如果已经加载了足够的内容,你可以根据需要停止加载
      if (receivedLength >= CHUNK_SIZE) {
        reader.cancel();
        break;
      }
    }
  }

  function processChunk(chunk) {
    // 在这里处理接收到的文章部分,例如,将其插入到 DOM 中
        setArticleContent((prevContent) => prevContent + chunk);
  }

  return (
    <div>
      <h1>Large Article</h1>
      <div>{articleContent}</div>
    </div>
  );
}

export default LargeArticle;

在这个 React 组件中,我们首先定义了一个 articleContent 状态,用于存储从后端接收到的文章内容。然后,我们使用 useEffect 钩子在组件挂载时调用 fetchArticleInChunks 函数,该函数负责发起 fetch 请求并处理分块响应。

与前面的示例类似,我们使用 ReadableStream API 逐个读取响应中的块,并在接收到足够的数据时停止加载。在 processChunk 函数中,我们将接收到的文章部分添加到 articleContent 状态中。

最后,我们在组件的渲染函数中显示加载到的文章内容。

ReadableStream 和 getReader() 方法简介

在我们的示例中,我们使用了 ReadableStream API 和 getReader() 方法来逐个处理分块响应。下面我们将简要介绍 ReadableStream 及其 getReader() 方法的相关概念。

ReadableStream 是 JavaScript 中的一种可读流对象,表示一个数据源的异步可读取接口。它可以用于处理从网络或其他数据源异步接收到的数据,例如,通过 fetch API 获取的 HTTP 响应。ReadableStream 具有许多方法和属性,用于控制数据的读取过程。

getReader() 方法是 ReadableStream 对象上的一个方法,用于创建并返回一个与该可读流相关联的 ReadableStreamDefaultReader 对象。ReadableStreamDefaultReader 提供了一组用于读取数据块的方法,使我们能够逐个处理从 ReadableStream 接收到的数据。

在我们的示例中,我们通过以下代码创建了一个 ReadableStreamDefaultReader

const reader = response.body.getReader();

随后,我们使用 read() 方法从 reader 中读取数据块。read() 方法返回一个包含 donevalue 属性的对象。done 属性表示流是否已经读取完毕,value 属性则包含从流中读取的数据块。通过在循环中不断调用 read() 方法,我们可以逐个处理数据块,直到整个流读取完毕或我们主动取消读取:

while (true) {
  const { done, value } = await reader.read();

  if (done) {
    break;
  }

  // 处理接收到的数据块
}

这种方法允许我们在数据可用时立即处理数据块,而无需等待整个响应加载完成。这有助于提高性能,特别是在处理大文件时。

转载自:https://juejin.cn/post/7211562512549724197
评论
请登录