基于Vue3.0 Node.js 的 大文件切片上传、秒传、断点续传实现方案梳理

news2025/2/28 6:17:08

✨💻 在处理大文件上传时,切片上传是提高效率与用户体验的关键技术之一。下面将详细介绍如何在前端利用Vue框架与Node.js后端配合,实现这一功能。

👆🏻大体流程

在这里插入图片描述

👆🏻一、文件切片上传

  1. 通过文件选择器获取用户选择文件
<template>
  <div>
   	<input label="选择附件" type="file" @change="handleFileChange" />
	<div @click="handleUpload">上传附件</div>
 </div>
</template>
<script>
     handleFileChange(e) {
     	this.container.file = e.target.files;
	 }
</script>
  1. 设定分片大小,将文件切片成多个文件块
<script>
	const SIZE = 10 * 1024 * 1024; // 切片大小
	// 生成文件切片
	createFileChunk(file, size = SIZE) {
	 const fileChunkList = [];
	  let cur = 0;
	  while (cur < file.size) {
	    fileChunkList.push({ file: file.slice(cur, cur + size) });
	    cur += size;
	  }
	  return fileChunkList;
	},
</script>
  1. 通过 spark-md5插件以及借助 web-worker 来并行生成文件的唯一Hash标识
<script>
//入参:fileChunkList ,对应 的文件切片数组
calculateHash(fileChunkList) {
     return new Promise(resolve => {
       this.container.worker = new Worker("/hash.js");
	   //将 fileChunkList 通过 postMessage 发送给 web-worker
       this.container.worker.postMessage({ fileChunkList });
	   //接收 web-worker 返回的消息
       this.container.worker.onmessage = e => {
         const { hash } = e.data;
         if (hash) {
           resolve(hash);
         }
       };
     });
   }
</script>

工具函数 hash.js
// 导入脚本
 self.importScripts("/spark-md5.min.js");
// 生成文件 hash 代码
self.onmessage = e => {
  const { fileChunkList } = e.data;
  const spark = new self.SparkMD5.ArrayBuffer();
  let count = 0;
  const loadNext = index => {
    const reader = new FileReader();
    reader.readAsArrayBuffer(fileChunkList[index].file);
    reader.onload = e => {
      count++;
      spark.append(e.target.result);
      if (count === fileChunkList.length) {
        self.postMessage({
          hash: spark.end()
        });
        self.close();
      }
      // 递归计算下一个切片
      loadNext(count);
    };
  };
  loadNext(0);
};
  1. 点击上传附件按钮 开始对大文件进行切片
// 调用后端接口上传切片
async uploadChunks() {
	 const requestList = this.data
	    .map(({ chunk,hash }) => {
	      const formData = new FormData();
	      formData.append("chunk", chunk);
	      formData.append("hash", hash);
	      formData.append("filehash", this.container.hash);
	      return { formData };
	    })
	    .map(async ({ formData }) =>
		//发起后端请求
	      axios.post({
	        url: "XXXXXXXX",
	        data: formData
	      })
	 );
	 await Promise.all(requestList); // 并发请求后端api
}

const async handleUpload() {
	if (!this.container.file) return;
	const fileChunkList = this.createFileChunk(this.container.file);
	this.container.hash = await this.calculateHash(fileChunkList);
	this.data = fileChunkList.map(({ file },index) => ({
	    chunk: file,
	    hash: this.container.hash + "-" + index // hash + 数组下标
	}));
	await this.uploadChunks();
}

👆🏻二、文件秒传

第一步:和文件切片前几步骤相同,先获取 改文件对应的文件切片 以及 hash 值

第二步:调用后端接口查询已经成功上传的文件hash值,如果都已经上传过该文件,前端显示上传完成

👆🏻 三、断点续传

第一步:和文件切片前几步骤相同,先获取 改文件对应的文件切片 以及 hash 值

第二步:调用后端接口查询已经成功上传的文件hash值,前端将已经上传过的hash值过滤得到未上传的文件hash值

第三步:将未上传的文件hash值列表,调用上传接口上传

👆🏻四、关于后端实现(Node版)

  1. 接收切片
const http = require("http");
const path = require("path");
const fse = require("fs-extra");
const multiparty = require("multiparty");
const server = http.createServer();
const UPLOAD_DIR = path.resolve(__dirname, "..", "target"); // 大文件存储目录
server.on("request", async (req, res) => {
  res.setHeader("Access-Control-Allow-Origin", "*");
  res.setHeader("Access-Control-Allow-Headers", "*");
  if (req.method === "OPTIONS") {
    res.status = 200;
    res.end();
    return;
  }
  // 使用 multiparty 包处理前端传来的 FormData
  // 在 multiparty.parse 的回调中,files 参数保存了 FormData 中文件,fields 参数保存了 FormData 中非文件的字段
  const multipart = new multiparty.Form();
  multipart.parse(req, async (err, fields, files) => {
    if (err) {
      return;
    }
    const [chunk] = files.chunk;
    const [hash] = fields.hash;
    const [filehash] = fields.filehash;
    const chunkDir = path.resolve(UPLOAD_DIR, filehash);
   // 切片目录不存在,创建切片目录
    if (!fse.existsSync(chunkDir)) {
      await fse.mkdirs(chunkDir);
    }
    // fs-extra 专用方法,类似 fs.rename 并且跨平台
    // fs-extra 的 rename 方法 windows 平台会有权限问题
    await fse.move(chunk.path, `${chunkDir}/${hash}`);
    res.end("received file chunk");
  });
});
server.listen(3000, () => console.log("正在监听 3000 端口"));
  1. 合并切片
//通过readStream流读文件
const pipeStream = (path, writeStream) =>
 new Promise(resolve => {
   const readStream = fse.createReadStream(path);
   readStream.on("end", () => {
     fse.unlinkSync(path);
     resolve();
   });
   readStream.pipe(writeStream);
 });
//合并切片
const mergeFileChunk = async (filePath, filehash, size) => {
 const chunkDir = path.resolve(UPLOAD_DIR, filehash);
 const chunkPaths = await fse.readdir(chunkDir);
 // 根据切片下标进行排序
 // 否则直接读取目录的获得的顺序可能会错乱
 chunkPaths.sort((a, b) => a.split("-")[1] - b.split("-")[1]);
 await Promise.all(
   chunkPaths.map((chunkPath, index) =>
     pipeStream(
       path.resolve(chunkDir, chunkPath),
       // 指定位置创建可写流
       fse.createWriteStream(filePath, {
         start: index * size,
         end: (index + 1) * size
       })
     )
   )
 );
 fse.rmdirSync(chunkDir); // 合并后删除保存切片的目录
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1841361.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

43、基于神经网络拟合函数的体脂估计(matlab)

1、神经网络拟合函数的原理及流程 神经网络拟合函数是一种基于人工神经元之间相互连接的模型&#xff0c;用来拟合复杂的非线性函数关系。其原理是通过多层次的神经元网络&#xff0c;每一层神经元通过激活函数将输入信号加权求和后输出&#xff0c;经过多次迭代优化权值&…

Java 集合框架详谈及代码分析(Iterable->Collection->List、Set->各接口实现类、Map->各接口实现类)

目录 Java 集合框架详谈及代码分析&#xff08;Iterable->Collection->List、Set->各接口实现类、Map->各接口实现类&#xff09;1、集合概述1-1&#xff1a;Java 集合概述1-2&#xff1a;List、Set、Map 三者的区别&#xff1f;1-3&#xff1a;集合框架底层数据结…

python电灯开关 青少年编程电子学会python编程等级考试三级真题解析2021年12月

python电灯开关 2021年12月 python编程等级考试级编程题 一、题目要求 1、编程实现 n个灯排成一排&#xff0c;开始时都是关着的。现进行如下操作: 所有电灯的按钮按动一次;所有编号为2的倍数的电灯按钮按动一次;所有编号为3的倍数的电灯的按钮按动一次: …所有编号为n-1的…

LeetCode 算法:删除链表的倒数第 N 个结点 c++

原题链接&#x1f517;&#xff1a;删除链表的倒数第 N 个结点 难度&#xff1a;中等⭐️⭐️ 题目 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a…

详细分析Element Plus的el-pagination基本知识(附Demo)

目录 前言1. 基本知识2. Demo3. 实战 前言 需求&#xff1a;从无到有做一个分页并且附带分页的导入导出增删改查等功能 前提一定是要先有分页&#xff0c;作为全栈玩家&#xff0c;先在前端部署一个分页的列表 相关后续的功能&#xff0c;是Java&#xff0c;推荐阅读&#x…

rockchip linux sdk指定编译配置文件

SDK&#xff1a;rk3568_linux4.19_V1.4.0 硬件平台&#xff1a;RK3566 一、指定板级配置文件 板级配置文件在<SDK>/device/rockchip/rk3566_rk3568目录下。 1、方法1 ./build.sh后⾯加上板级配置⽂件&#xff0c;例如&#xff1a; ./build.sh /device/rockchip/rk3…

STM32微控制器库指南:函数特性、应用范围与实践

在嵌入式系统的设计和开发中&#xff0c;STM32系列微控制器因其卓越的处理能力和多样的外设选项而广受推崇。STM32库函数作为开发流程中不可或缺的工具&#xff0c;扮演着至关重要的角色。本文将详细阐述STM32库函数的主要特性、应用场景及其在实际开发中的应用实例。 什么是ST…

Golang | Leetcode Golang题解之第168题Excel表列名称

题目&#xff1a; 题解&#xff1a; func convertToTitle(columnNumber int) string {ans : []byte{}for columnNumber > 0 {columnNumber--ans append(ans, Abyte(columnNumber%26))columnNumber / 26}for i, n : 0, len(ans); i < n/2; i {ans[i], ans[n-1-i] ans[n…

JavaScript基础部分知识点总结(Part3)

函数的概念 1. 函数的概念 在JS 里面&#xff0c;可能会定义非常多的相同代码或者功能相似的代码&#xff0c;这些代码可能需要大量重复使用。虽然for循环语句也能实现一些简单的重复操作&#xff0c;但是比较具有局限性&#xff0c;此时我们就可以使用JS 中的函数。函数&…

RabbitMQ实践——交换器(Exchange)和绑定(Banding)

大纲 direct型交换器默认交换器命名交换器 fanout型交换器topic型交换器headers型交换器 RabbitMQ在概念上由三部分组成&#xff1a; 交换器&#xff08;Exchange&#xff09;&#xff1a;负责接收消息发布者发布消息的结构&#xff0c;同时它会根据“绑定关系”&#xff08;Ba…

PySide(PyQt)的特殊按钮(互锁、自锁、独占模式)

界面图&#xff1a; Qt Designer中创建窗口&#xff0c;放置一个QGroupBox&#xff0c;命名为btnStation&#xff0c;这就是自定义的按钮站&#xff0c;按钮站里放置6个按钮。自锁按钮相当于电器中的自锁功能的按钮&#xff0c;每按一次状态反转并保持不变。独占按钮也是自锁功…

使用asyncua模块的call_method方法调用OPC UA的Server端方法报错:asyncio.exceptions.TimeoutError

使用asyncua模块的call_method方法调用OPC UA的Server端方法报错&#xff1a;asyncio.exceptions.TimeoutError 报错信息如下&#xff1a; Traceback (most recent call last): asyncio.run(main()) File “D:\miniconda3\envs\py31013\lib\asyncio\runners.py”, line 44, in…

Python酷库之旅-比翼双飞情侣库(17)

目录 一、xlwt库的由来 1、背景和需求 2、项目启动 3、功能特点 4、版本兼容性 5、与其他库的关系 6、示例和应用 7、发展历史 二、xlwt库优缺点 1、优点 1-1、简单易用 1-2、功能丰富 1-3、兼容旧版Excel 1-4、社区支持 1-5、稳定性 2、缺点 2-1、不支持.xls…

什么是无限铸币攻击?它是如何运作的?

一、无限铸币攻击解释 无限铸币攻击是指攻击者操纵合约代码不断铸造超出授权供应限制的新代币。 这种黑客行为在去中心化金融 (DeFi) 协议中最为常见。这种攻击通过创建无限数量的代币来损害加密货币或代币的完整性和价值。 例如&#xff0c;一名黑客利用了 Paid 网络的智能…

第三十三章 添加和使用自定义标题元素

文章目录 第三十三章 添加和使用自定义标题元素SOAP 标头元素简介如何表示 SOAP 标头 第三十三章 添加和使用自定义标题元素 本主题介绍如何添加和使用自定义 SOAP 标头元素。 有关发生故障时添加标头元素的信息&#xff0c;请参阅 SOAP 故障处理。 WS-Addressing 标头元素在…

Java swing JTable 示例

代码&#xff0c; import java.awt.Container; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTable;public class Mylmlk {public static void main(String[] agrs){JFrame framenew JFrame("学生成绩表");frame.setSize(500,2…

it职业生涯规划系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;职业介绍管理&#xff0c;答题管理&#xff0c;试题管理&#xff0c;基础数据管理 前台账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;在线答题&#xff0…

QT基础 - QMainWindow主窗口

目录 零. 简介 一. 菜单栏 二. 工具栏 三. 状态栏 四. 可停靠区域 五. 总结 零. 简介 QMainWindow 是 Qt 中用于构建主窗口的类。 它通常包含以下几个主要部分&#xff1a; 菜单栏&#xff1a;用于提供各种操作选项。工具栏&#xff1a;放置常用的操作按钮。中心区域&…

DAMA学习笔记(二)-数据治理

1.引言 数据治理&#xff08;Data Governance&#xff0c;DG&#xff09;的定义是在管理数据资产过程中行使权力和管控&#xff0c;包括计划、监控和实施。在所有组织中&#xff0c;无论是否有正式的数据治理职能&#xff0c;都需要对数据进行决策。建立了正式的数据治理规程及…

【STM32-ST-Link】

STM32-ST-Link ■ ST-Link简介■ ST-Link驱动的安装。■ ST-Link编程软件(MDK)配置。■ ST-Link固件升级方法 ■ ST-Link简介 由于德产 J-LINK 价格非常昂贵&#xff0c; 而国产 J-LINK 因为版权问题将在万能的淘宝销声匿迹。 所以我们有必要给大家介绍 JTAG/SWD 调试工具中另…