“如何优化大文件上传?”,“用Web Worker!”

"如何优化大文件上传?","用Web Worker!"

前记

"师兄,你们之前开发的项目里面的文件上传老是上传的很慢,有时候还不成功,感觉体验不大好"。这是前段时间学校工作室的师弟跟我说的一句话,我还记得之前这部分内容是直接扔在了主线程然后用切片上传去做的,之前上传的文件很小,但随着时间的推移,上传的文件越来越大,导致上传的时候主线程开始堵塞,以至于体验开始下滑。这个时候,我在实习公司的群里看到了Web Worker,灵感也油然而生。

web worker 是什么

用官方的话说: Web Worker 是一种在浏览器中运行的 JavaScript 脚本,可以在独立的线程中今执行,与主线程并行工作,提供了一种在后台执行复杂计算或处理耗时操作的方式,而不会堵塞主线程的执行。

用我自己的理解 :就是"开小灶",众所周知,Js是一种单线程语言,在传统的 Js 环境中,所有代码都运行在一个主线程中,包括处理用户界面,js代码执行和网络请求。当执行耗时操作时,就会导致用户页面的卡顿和不响应。而 Web Worker 的出现允许让开发者将耗时操作放在独立的线程中执行进而不会堵塞主线程,它还可以与主线程进行通信,通过信息传递机制来交换数据和结果。总的来说,Web Worker就是在浏览器"空闲"的时候开出一条独立于主线程的新线程,并通过"某种手段"实现与主线程通信,在不堵塞主线程的同时还提高了用户的体验。

使用

Web Worker 面对解决大文件上传的步骤

  • 创建一个Web Worker:在主线程上使用new Worker()构造函数创建一个Web Worker示例。指定Web Worker脚本的URL,该脚本在后台执行.
  • Web Worker脚本中处理文件上传处理:在Web Worker脚本中,使用onmessage事件监听主线程发送的消息。当收到上传文件的数据时,Web Worker可以将文件数据分块处理,并将每个块上传到服务器.
  • 与主线程进行通信:Web Worker可以使用postMessage()方法将处理结果发送回主线程。eg: 可以发送上传进度、成功或失败的信息.
  • 监听Web Worker的消息:在主线程中,可以使用onmessage事件监听Web Worker发送的消息,根据接收到的消息,可以更新上传进度、显示成功或失败的消息等.

关键代码

  • 创建FileUploader组件
jsx 复制代码
import React, { useState } from "react";

const FileUploader = () => {
  const [progress, setProgress] = useState(0);

  const handleFileUpload = () => {
    const worker = new Worker("worker.js");

    worker.onmessage = (event) => {
      const { type, payload } = event.data;

      if (type === "progress") {
        setProgress(payload);
      } else if (type === "success") {
        console.log("File upload successful");
        // 处理上传成功的逻辑
      } else if (type === "error") {
        console.error("File upload failed");
        // 处理上传失败的逻辑
      }
    };

    // 获取要上传的文件
    const file = document.getElementById("inputFile").files[0];

    // 发送文件给Web Worker进行上传
    worker.postMessage(file);
  };

  return (
    <div>
      <input type="file" id="inputFile" />
      <button onClick={handleFileUpload}>上传文件</button>
      <div>上传进度:{progress}%</div>
    </div>
  );
};

export default FileUploader;
  • 创建Web Worker脚本
jsx 复制代码
self.onmessage = (event) => {
  const file = event.data;
  const CHUNK_SIZE = 1024 * 1024; // 每个分块的大小,这里设置为1MB

  const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
  let uploadedChunks = 0;

  const uploadChunk = (chunk) => {
    // 模拟上传过程,这里使用setTimeout来模拟异步上传
    setTimeout(() => {
      uploadedChunks++;
      const progress = Math.floor((uploadedChunks / totalChunks) * 100);
      self.postMessage({ type: "progress", payload: progress });

      if (uploadedChunks === totalChunks) {
        self.postMessage({ type: "success" });
      } else {
        // 继续上传下一个分块
        uploadChunk();
      }
    }, 1000);
  };

  // 开始上传第一个分块
  uploadChunk();
};

上面的代码示例中,FileUploader组件包含一个文件选择框和一个上传按钮,当点击上传按钮,通过new Worker()创建一个Wrb Worker示例,将文件数据传递给Web Worker。Web Worker在后台执行文件上传逻辑,并通过postMessage()方法将上传进度和结果发送回到主线程。主线程通过监听Web Worker的消息来更新上传进度,并根据上传结果执行相应的逻辑.

  • webpack 配置相关插件

    使用worker-loader配置

    js 复制代码
      module.export = {
          // ...
          module:{
              rules:[
                  {
                      test:/\.worker\.js$/,
                      use:{ loader: 'worker-loader' }
                  }
              ]
          }
      }

    上面配置项将匹配以.worker.js结尾的文件,并使用worker-loader来处理。在代码中,可以通过new Worker()语法来创建Web Worker示例.

    使用inline-loader配置

    js 复制代码
     module.export = {
          // ...
          module:{
              rules:[
                  {
                      test:/\.worker\.js$/,
                      use:[
                      { loader: 'worker-loader' },
                      { loader: 'inline-loader' }
                      ]
                  }
              ]
          }
      }

    上述配置将使用worker-loader加载Web Worker脚本,并使用inline-loader内联脚本。在代码中,可以直接使用import语法引入Web Worker脚本.

注意点

  • Web Worker不能直接访问DOM,因此在处理文件上传时,可能需要将文件数据转换为ArrayBufferBlob等格式进行处理.
  • Web Worker有两种类型:Dedicated WorkerShared Worker。Dedicated Worker只能被创建它的脚本所使用,而Share Worker可以被多个脚本共享。无论是哪种类型的Web Worker,它们都有自己的全局作用域,可以执行JavaScript代码,并且可以通过postMessage()方法发送消息给创建它们的上下文,并通过onmessage时间监听来接受消息.
  • 在实际使用Web Worker进行大文件上传之前,建议进行充分的测试和性能优化。可以模拟各种网络条件和文件大小,评估上传速度、内存使用和用户体验,并根据测试结果进行优化.
  • 如果需要支持断点续传功能,可以在上传过程中记录已上传的块,以便在上传中断后能够从上次中断的地方继续上传。这可以通过将已上传的块信息保存在浏览器的本地存储或服务器端进行记录来实现。

待续

相关推荐
再吃一根胡萝卜5 小时前
08 · 前端:Vue3 + SSE 流式与执行链路可视化
面试
再吃一根胡萝卜5 小时前
03 · 后端:FastAPI 与分层架构
面试
mldong7 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排7 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60618 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角9 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!9 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰9 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
Interview Aid1129 小时前
Walmart Global Tech SDE 三轮面经|基础、并发、压力面
面试·职场和发展
mayaairi11 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js