当前位置: 首页 > news >正文

web-worker应用在大文件切片上传

当文件体积过大时,传统的文件上传方式往往会导致页面卡顿,用户体验不佳。为了解决这一问题,我们可以利用Web Worker技术来进行大文件的切片上传。本文将详细介绍如何使用Web Worker进行大文件切片上传,并通过具体的例子来演示其实现过程。

Web Worker简介

Web Worker是Web浏览器提供的一种在后台线程中运行JavaScript的功能。它独立于主线程运行,可以执行计算密集型或长时间运行的任务,而不会阻塞页面的渲染和交互。通过将大文件切片上传的逻辑放在Web Worker中执行,我们可以充分利用浏览器的多线程能力,提高上传速度,并保持页面的流畅运行。

Web Worker基于Vue的基础用法

在Vue项目中配置webpack来使用web-worker涉及几个关键步骤。这主要涉及到处理worker文件的加载,确保它们被正确地打包和引用。以下是一个基本的配置过程:

1.安装worker-loader

首先,你需要安装worker-loader,这是一个webpack的loader,用于处理worker文件。

npm install --save-dev worker-loader
2.配置webpack
module.exports = {publicPath: './',chainWebpack: config => {  config.module  .rule('worker')  .test(/.worker.js$/)  // 如果需要.worker.js后缀.use('worker-loader')  .loader('worker-loader').options({ // 可以查阅worker-loader文档,根据自己的需求进行配置})}  
}
3.创建和使用worker

创建一个worker文件,并给它一个.worker.js的扩展名。例如,你可以创建一个my-worker.worker.js文件。

// my-worker.worker.js  
self.onmessage = function(e) {  console.log('Worker: Hello World');  const result = doSomeWork(e.data);  self.postMessage(result);  
};  function doSomeWork(data) {  // 模拟一些工作  return data * 2;  
}

在你的Vue组件或其他JavaScript文件中,你可以像下面这样创建一个worker实例:

// MyComponent.vue 或其他.js文件  
import MyWorker from './my-worker.worker.js';  export default {  methods: {  startWorker() {  const myWorker = new MyWorker();  myWorker.onmessage = (e) => {  console.log('Main script: Received result', e.data);  };  myWorker.postMessage(100); // 发送数据给worker  }  },  mounted() {  this.startWorker();  }  
};

现在,当组件被挂载时,它将启动worker,发送一个消息,并在收到worker的响应时打印结果。

接下来我们进行实战,利用web-worker的机制进行大文件切片上传

实战:实现大文件切片上传

1.逻辑梳理
  1. 文件切片:使用 JavaScript 的 Blob.prototype.slice() 方法将大文件切分成多个切片。
  2. 上传切片:使用 axios 或其他 HTTP 客户端库逐个上传切片。可以为每个切片生成一个唯一的标识符(例如,使用文件的哈希值和切片索引),以便后端能够正确地将它们合并。
  3. 客户端线程数:获取用户CPU线程数量,以便最大优化上传文件速度。
  4. 控制上传接口的并发数量:防止大量的请求并发导致页面卡死,设计一个线程队列,控制请求数量一直保持在6。
2.实现

我会在文章后面放demo的GitHub源码。

1.获取客户端线程数量

navigator.hardwareConcurrency 是一个只读属性,它返回用户设备的逻辑处理器内核数。

export const getConcurrency = () => navigator.hardwareConcurrency || 4 // 浏览器不支持就默认4核
2.主线程

定义和处理一些必要的常量,并且根据用户的线程数进行开启多线程Web-worker任务处理文件切片。

import { defer, createEventHandler } from 'js-hodgepodge'
import FileWorker from './files.worker'export const getConcurrency = () => navigator.hardwareConcurrency || 4export const handleEvent = () => createEventHandler('handleSchedule')export const sliceFile = file => {const dfd = defer()const chunkSize = 1024 // 1Kbconst thread = getConcurrency() // 线程数const chunks = []const chunkNum = Math.ceil(file.size / chunkSize) // 切片总数量const workerChunkCount = Math.ceil(chunkNum / thread) // 每个线程需要处理的切片数量let finishCount = 0;for (let i = 0; i < thread; i++) {const worker = new FileWorker()// 计算每个线程的开始索引和结束索引const startIndex = i * workerChunkCount;let endIndex = startIndex + workerChunkCount;// 防止最后一个线程结束索引大于文件的切片数量的总数量if (endIndex > chunkNum) {endIndex = chunkNum;}worker.postMessage({file,chunkSize,startIndex,endIndex,});worker.onmessage = (e) => {// 接收到 worker 线程返回的消息for (let i = startIndex; i < endIndex; i++) {chunks[i] = {...e.data[i - startIndex],chunkNum,filename: file.name};}worker.terminate(); // 关闭线程finishCount++;if (finishCount === thread) {dfd.resolve({chunks,chunkNum});}};}return dfd
}
3.实现文件切片

首先,我们需要创建一个 Web Worker 脚本,用于处理文件切片和切片hash

import md5 from 'js-md5'self.onmessage = async function ({data: {file,chunkSize,startIndex,endIndex,}
}) {const arr = [];for (let i = startIndex; i < endIndex; i++) {arr.push(createChunks(file, i, chunkSize));}const chunks = await Promise.all(arr)// 提交线程信息postMessage(chunks);
}const createChunks = (file,index,chunkSize
) => {return new Promise((resolve) => {// 开始第几个*分片的大小const start = index * chunkSize;// 结束时start + 分片的大小const end = start + chunkSize;const fileReader = new FileReader();// 每个切片都通过FileReader读取为ArrayBufferfileReader.onload = (e) => {const content = new Uint8Array(e.target.result);const files = file.slice(start, end);const md5s = md5.arrayBuffer(content)function arrayBufferToHex(buffer) {let bytes = new Uint8Array(buffer);let hexString = '';for (let i = 0; i < bytes.byteLength; i++) {let hex = bytes[i].toString(16);hexString += hex.length === 1 ? '0' + hex : hex;}return hexString;}resolve({start,end,index,hash: arrayBufferToHex(md5s),  // 生成唯一的hashfiles,});};// 读取文件的分片fileReader.readAsArrayBuffer(file.slice(start, end));});
}

Web Worker通过onmessage事件接收消息。当主线程发送消息时,这个消息会作为参数传递给onmessage函数。

切片hash处理流程:使用FileReader来读取文件内容。当文件分片读取完毕后,会触发onload这个事件,使用new Uint8Array(e.target.result)将读取的ArrayBuffer转换为Uint8Array,再利用js-md5的使用md5.arrayBuffer(content)计算分片的MD5哈希值,使用arrayBufferToHex函数将切片buffer转换为十六进制String,当所有分片处理完毕后,将结果(即分片及其相关信息)发送postMessage回主线程。

4.请求池的设计与处理

我这里创建一个请求队列,并使用 Promise 来控制并发请求的数量。创建一个数组来存储待处理的请求,并使用 Promise 来控制每次只有一定数量的请求被发送。当某个请求完成时,再从队列中取出下一个请求来发送。

export const uploadFile = (chunks // 总切片
) => {chunks = chunks || []let schedule = 0 // 进度const { dispatch } = handleEvent()const requestQueue = (concurrency) => {concurrency = concurrency || 6const queue = [] // 线程池let current = 0const dequeue = () => {while (current < concurrency && queue.length) {current++;const requestPromiseFactory = queue.shift();requestPromiseFactory().then(result => { // 上传成功处理console.log(result)schedule++; // 收集上传切片成功的数量dispatch(window, schedule);  // 事件派发,通知进度}).catch(error => { // 失败console.log(error)}).finally(() => {current--;dequeue();});}}return (requestPromiseFactory) => {queue.push(requestPromiseFactory)dequeue()}}const handleFormData = obj => {const formData = new FormData()Object.entries(obj).forEach(([key, val]) => {formData.append(key, val)})return formData}const enqueue = requestQueue(6)for (let i = 0; i < chunks.length; i++) {enqueue(() => axios.post('/api/upload',handleFormData(chunks[i]),{headers: {'Content-Type': 'multipart/form-data' }}))}return schedule}

利用了第三方库js-hodgepodge的发布订阅,将上传切片成功的数量发布给主界面,得到相应的上传进度。

可以参考这个内容:

在创建CustomEvent对象时,通常需要指定事件的类型(type)以及一个可选的事件初始化字典(eventInitDict),后者用于设置事件的详细属性。一旦创建了CustomEvent对象,就可以使用dispatchEvent方法将其触发,进而在事件监听器中进行处理。

基本用法

下面是一个简单的示例,展示了如何使用window.CustomEvent来创建一个自定义事件并触发它:

// 创建一个自定义事件  
const myEvent = new CustomEvent('myCustomEvent', {  detail: {  message: 'Hello, World!'  },  bubbles: true,  cancelable: true  
});  // 触发自定义事件  
window.dispatchEvent(myEvent);  // 监听自定义事件  
window.addEventListener('myCustomEvent', function(event) {  console.log(event.detail.message); // 输出: Hello, World!
});

CustomEvent接收两个参数:

  1. type:一个字符串,表示事件的名称。
  2. eventInitDict:一个配置事件的选项对象,是可选的。这个对象可以包含以下属性:
    • bubbles:一个布尔值,表示事件是否冒泡。默认为false
    • cancelable:一个布尔值,表示事件是否可以被取消。默认为false
    • detail:包含传递给事件监听器的任何自定义数据的对象。
接下来我们实现属于自己的发布订阅工具函数吧

我这里是基于TS封装的一个发布订阅函数,接下来我们来解析它:

function createEventHandler<DataType>(name: string) {const addEventListener = (Win: Window,fn: (e: { detail: DataType }) => void) => {// @ts-ignoreWin.addEventListener(name, fn)// @ts-ignoreconst eject = () => Win.removeEventListener(name, fn)return eject}const dispatch = (Win: Window,data: DataType) => {Win.dispatchEvent(new CustomEvent(name, { detail: data }))}return {addEventListener,dispatch}}

我们定义了一个名叫createEventHandler的函数并暴露发布(dispatch)订阅(addEventListener) 这两个方法:

  1. addEventListener: 接收两个参数,一个是Window对象,一个是事件订阅的回调。
  2. dispatch: 接收两个参数,一个是Window对象,一个是发布事件所需的内容。

用法:

const { addEventListener, dispatch } = createEventHandler('handleMessage')const eject = addEventListener(window, ({ detail }) => {console.log(detail); // { message: 1111 }})setTimeout(() => {dispatch(window, { message: 1111 })// 当不再需要事件监听器时,可以调用 eject 函数来移除它  eject();})

在这个示例中,我们创建了一个名为 'handleMessage' 的自定义事件,其 detail 属性包含对象 { message: 1111 }。我们添加了一个事件监听器来打印这个对象,并在之后触发了这个事件。最后,我们使用返回的 eject 函数移除了事件监听器。

CustomEvent在Web开发中非常有用,尤其是在需要实现组件间通信或处理特定业务逻辑时。通过自定义事件,开发者可以更精确地控制事件的触发和响应,从而实现更复杂的交互和功能。

需要注意的是,由于CustomEvent是DOM API的一部分,因此它主要在浏览器环境 (大部分浏览器都支持) 中使用。在非浏览器环境 (如Node.js) 中,可能无法使用CustomEvent或需要使用类似功能的库或工具。

7.主界面代码
<template><div><input type="file" ref="file"><button @click="handleUpload">提交</button><p>进度:{{ progress * 100 }}%</p></div>
</template><script>import { sliceFile, uploadFile, handleEvent } from './file.utils'export default {data() {return {progress: 0}},methods: {async handleUpload() {const file = this.$refs.file.files[0]if(!file) {return}console.time()const dfd = sliceFile(file)dfd.promise.then(({ chunks, chunkNum }) => {uploadFile(chunks)const { addEventListener } = handleEvent()const eject = addEventListener(window, ({ detail: schedule }) => {this.progress = schedule / chunkNumif(schedule === chunkNum) { // 上传完成,关闭事件监听eject()}})})console.timeEnd() }}}
</script><style></style>
6.执行响应结果打印

当执行一个大文件上传时,时间可被大大的压缩了。

node后端切片与组合结果其实整个流程比较重要的就是文件切片,和请求池的设计,具体项目细节请查看源码

相关文章:

web-worker应用在大文件切片上传

当文件体积过大时&#xff0c;传统的文件上传方式往往会导致页面卡顿&#xff0c;用户体验不佳。为了解决这一问题&#xff0c;我们可以利用Web Worker技术来进行大文件的切片上传。本文将详细介绍如何使用Web Worker进行大文件切片上传&#xff0c;并通过具体的例子来演示其实…...

Django 模板分割及多语言支持案例【需求文档】-->【实现方案】

Django 模板分割及多语言支持案例 这个案例旨在提供一个清晰的示范&#xff0c;展示如何将复杂的页面分解为多个可复用的模板组件&#xff0c;使代码更加模块化和易于管理。希望这篇案例文章对你有所帮助。 概述 在 Django 项目开发中&#xff0c;使用模板分割和多语言支持能…...

C中设计不允许继承的类的实现方法是什么?

在C中&#xff0c;设计不允许继承的类可以通过多种方法实现。以下是详细的方法说明及示例&#xff1a; ### 方法一&#xff1a;将构造函数和析构函数设为私有 这种方法的核心思想是通过将构造函数和析构函数设为私有&#xff0c;使得子类无法调用这些函数&#xff0c;从而无法…...

面对小白的C语言学习方法

这是第20篇文章&#xff0c;不来弄一些技术的&#xff0c;弄一些最近的学习心得&#xff0c;怎么更有效地自学C语言 书籍 书籍可以很有效的告知我们专有函数&#xff0c;使用方法还有一些思考方式&#xff0c;缺点是实操差点意思&#xff0c;还是不太能解决实际问题&#xff…...

使用libgif库解码全过程(C语言)-包括扩展块的处理

我看到的所有例程&#xff0c;都把扩展部分的处理跳过了&#xff0c;而我的动画是有透明度的&#xff0c;这就导致解码后的图像在有透明色的像素部分&#xff0c;呈现了很多的黑点&#xff0c;或者闪白的情况出现。经过调试&#xff0c;终于成功。 文件格式 先了解一下GIF的文…...

blazor实现ASP.NET网站用户批量注册方法

ASP.NET网站用户批量注册是许多使用blazor系统开发遇到的问题,为了解决这个问题,我们提出比较完善的解决方法,通过代码实现了一个批量用户注册功能,用于解析一份用户名列表,并通过后台服务注册用户,同时对成功和失败的注册进行记录和反馈。以下是实现功能的详细工作原理描…...

SpringCloud 入门(4)—— 网关

上一篇&#xff1a;SpringCloud 入门&#xff08;3&#xff09;—— Nacos配置中心-CSDN博客 Spring Cloud Gateway 作为 Spring Cloud 生态系统的一部分&#xff0c;主要在微服务架构中充当 API 网关的角色。它提供了统一的入口点来处理所有的 HTTP 请求&#xff0c;并将这些请…...

什么是WebAssembly?怎么使用?

一、简述 WebAssembly&#xff0c;也称为Wasm&#xff0c;是基于堆栈的虚拟机的二进制指令格式。它被设计为一个可移植的目标&#xff0c;用于编译C、C和Rust等高级编程语言&#xff0c;允许代码以接近本机速度在web浏览器中运行。WebAssembly于2015年由包括谷歌、微软、Mozill…...

v3s点RGB屏 40pin 800x480,不一样的点屏,不通过chosen。

一、背景、目的、简介。 一般来说&#xff0c;通过uboot将屏幕参数传给kernel&#xff0c;是通过修改设备树。 uboot和kernel都需要屏幕点亮。uboot侧重于显示一张图片。而kernel则多是动画。 在这里&#xff0c;我先是找到了一个裸机点屏的代码。将其编译成静态库后&#x…...

某科技局国产服务器PVE虚拟化技术文档

环境介绍 硬件配置 服务器品牌&#xff1a;黄河 型号&#xff1a;Huanghe 2280 V2 Cpu型号&#xff1a;kunpeng-920 磁盘信息 :480SSD * 2 ,4T*4 网卡&#xff1a;板载四口千兆 如下表 四台服务器同等型号配置&#xff0c;均做单节点虚拟化&#xff0c;数据保护采用底层r…...

中科岩创边坡自动化监测解决方案

行业现状 由于边坡不稳定性因素&#xff0c;可能会造成斜坡上的岩土体沿着某个面不均匀向下向外滑动&#xff0c;形成滑坡&#xff1b;陡峭山坡上岩土体在重力作用下&#xff0c;发生陡然倾落运动&#xff0c;造成崩塌&#xff1b;在沟谷或山坡上产生的夹带大量泥沙、石块等固体…...

GPT-O3:简单介绍

GPT-O3&#xff1a;人工智能领域的重大突破 近日&#xff0c;OpenAI发布了其最新的AI模型GPT-O3&#xff0c;这一模型在AGI评估中取得了惊人的成绩&#xff0c;展现出强大的能力和潜力。GPT-O3的出现标志着人工智能领域的重大进步&#xff0c;预计将在2025年实现更大的突破。 …...

cudnn版本gpu架构

nvcc --help 可以看 --gpu-architecture 写到的支持的架构 NVIDIA 的 GPU 架构是按代次发布的&#xff0c;以下是这些架构的对应说明&#xff1a; NVIDIA Hopper: 这是 NVIDIA 于 2022 年推出的架构之一&#xff0c;面向高性能计算&#xff08;HPC&#xff09;和人工智能&…...

数据库安全-redisCouchdb

1.redis未授权访问 默认端口:6379 1.1 Redis沙盒逃逸漏洞RCE-CVE-2022-0543 介绍&#xff1a;Redis 是一套开源的使用 ANSI C编写、支持网络、可基于内存亦可持久化的日志型、键值存储数据库&#xff0c;并提供多种语言的API。Redis 如果在没有开启认证的情况下&#xff0c;…...

ubuntu22.04安装PaddleX3

PaddleOCR 安装过程可以参考PaddleX本地安装教程 我的电脑环境配置&#xff1a; ubuntu22.04 cuda11.8&#xff08;之前安装的是12.4没有匹配的paddle-gpu;这里改成11.8&#xff09; 一、安装基础环境 1、 conda create -n ppx1 python3.10 2、 conda activate ppx1 3、…...

Flutter 实现全局悬浮按钮学习

Flutter 代码如何实现了一个全局悬浮按钮&#xff0c;当点击按钮时&#xff0c;会显示一个可以拖动并且通过长按可以移除的悬浮控件。 前置知识点学习 Offset Offset 是 Flutter 中的一个类&#xff0c;用于表示二维平面中的位置或位移。它通常用于描述坐标系中的一个点&…...

14-C语言多文件编程

一、各种变量 在学习多文件编程之前&#xff0c;先要了解清楚各种变量的作用范围以及生命周期。 1.普通变量 1.1普通局部变量 定义形式&#xff1a;在复合语句{}里面定义的变量为普通局部变量&#xff1b;作用范围&#xff1a;在复合语句{}里面有效&#xff1b;生命周期&am…...

基于Springboot的在线问卷调查系统【附源码】

基于Springboot的在线问卷调查系统 效果如下&#xff1a; 系统主页面 问卷列表页面 个人中心页面 系统登陆页面 管理员主页面 问卷管理页面 研究背景 随着互联网技术的飞速发展&#xff0c;传统的问卷调查方式因其时间和地点的限制&#xff0c;难以高效地收集到足够的数据。…...

Redis热点数据管理全解析:从MySQL同步到高效缓存的完整解决方案

1. 引言 1.1 背景介绍&#xff1a;MySQL与Redis在高性能场景下的结合 在现代互联网应用中&#xff0c;MySQL作为关系型数据库&#xff0c;承担了大量业务数据的存储任务。然而&#xff0c;随着业务的增长&#xff0c;海量数据的查询性能成为一个瓶颈。为了应对高并发和低延迟…...

【图书介绍】】几本Linux C\C++编程图书

Linux C\C编程&#xff0c;是IT领域比较稳定的职业发展方向&#xff0c;本文介绍几本Linux开发方面的图书。 《Linux C与C一线开发实践&#xff08;第2版&#xff09;》 《Linux C与C一线开发实践&#xff08;第2版&#xff09;&#xff08;Linux技术丛书&#xff09;》(朱文…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问&#xff08;基础概念问题&#xff09; 1. 请解释Spring框架的核心容器是什么&#xff1f;它在Spring中起到什么作用&#xff1f; Spring框架的核心容器是IoC容器&#…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...