js文件上传 分片上传/断点续传/极速秒传
(极速秒传)利用md5判断上传的文件是否存在
MD5信息摘要算法,一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。
每一个文件都会生成一个不同的md5编码 例:【3a94b8ca53dea8524d16c4e39dd43a69】
优点
服务器中不会出现重复文件
极速秒传
传输文件先校验md5,减小服务器压力
思路是
在文件上传到服务器前,将文件进行MD5转换,然后将转换完后的MD5码传给服务器,服务器判断当前MD5码是否存在,如果存在就代表着服务器上已经有了跟该文件相同的文件,不再需要上传文件
// 获取md5
import SparkMD5 from 'spark-md5';const getMd5 = async (file: File) => {let text = await file.text(); // 将文件转换成text文本return SparkMD5.hashBinary(text); // 通过插件进行MD5加密
}
分片上传
分片上传就是把一个大文件,按照一定的大小,分割成多个小文件分别进行上传,文件上传结束后,服务器对所有的文件进行合并(前端负责拆分、后端负责整合)
优点(分片上传/断点续传)
上传速度快
上传稳定性强
降低传输断开风险
前端拆分文件的方法
前端拿到【file】文件后,可以调用file.slice方法(Blob的方法)对文件进行拆分
const BIGSIZE = 1024 * 1024 * 2 // 2mb// 3. 切割文件
const getFileList = (file: File): FileList[] => {let cur = 0; // 当前分割到的位置const fileChunkList: any[] = [] // 最终的blob数组while (cur < file.size) {let chunk = file.slice(cur, cur + BIGSIZE)fileChunkList.push({chunk: chunk,index: fileChunkList.length})cur += BIGSIZE;}return fileChunkList
}
服务器合并文件的方法(node示例)
filesNameList = ['分片文件a','分片文件b',...] // 文件路径
// 创建可写流
let writeStream = fs.createWriteStream('最终合并后的文件路径', {flags: 'w+',encoding: 'base64',
})
// 合并文件
for (let i = 0; i < filesName.length; i++) {// 读取文件let val = await getFile(filesNameList[i])// 写入文件writeStream.write(val)
}
writeStream.end();// 读取文件函数
const getFile = (path) => {return new Promise(resolve => {// 创建可读流const readStream = fs.createReadStream(path, {flags: 'r',encoding: 'base64',});var count = 0;var str = '';readStream.on('data', (data) => { //监听str += datacount++})readStream.on('end', () => { //读取结束resolve(str)})});
}
断点续传
断点续传就是在分片上传逻辑的基础上,增加断开后继续上传的逻辑
一种实现思路是:可以将分片的文件命名为对应的索引,在文件断开后,根据索引判断有多少分片已经上传成功了,然后继续传输没有上传成功的分片
附代码
前端:
bigUpFile(file)
/***********************样式与提示***********************/
// 大文件上传中
const bigFileLoading = ref(false)
const loadingTextArr = ref<string[]>([])const addText = (str: string, number: undefined | number = undefined, type = 'default') => {loadingTextArr.value.push(str)if (number || number === 0) {if (type === 'add') {progressNumber.value += number} else {progressNumber.value = number}}
}/***********************上传代码***********************/
// 开始函数
const bigUpFile = async (file: File) => {addText('大文件上传中...', 0)bigFileLoading.value = true// 1 获取文件的md5addText('获取文件md5...', 5)const fileMD5 = await getMd5(file) // md5// 2. 极速秒传addText('判断是否可以进行极速秒传...', 10)let upEnd = await fastUpFile(file, fileMD5)if (upEnd) {addText('极速秒传成功!', 100)message.success('极速秒传成功!')emit("getList");return}addText('开始分割文件...', 15)// 3. 切割文件let fileChunkList = await getFileList(file)let fileNumber = fileChunkList.length // 文件分段总数addText('校验断点续传...', 18)// 4. 校验大文件分片上传文件数量let execFileList = await getExecFileList(fileMD5)if (execFileList && execFileList?.length) {let fl: FileList[] = []fileChunkList.forEach(item => {if (!execFileList.includes(item.index) && !execFileList.includes(item.index + '')) {fl.push(item)}})fileChunkList = fladdText('校验断点续传成功,继续传输...', 18)}addText('开始上传文件...', 20)// 5. 开始上传任务await createUpBigFile(fileChunkList, fileMD5, file.name, fileNumber)addText('结束...', 100)
}/**********************************************/// 1 获取文件的md5
const getMd5 = async (file: File | Blob) => {let buffer = await file.text(); // 获取bufferreturn SparkMD5.hashBinary(buffer);
}// 2. 极速秒传
const fastUpFile = async (file: File, fileMD5: string) => {// 发送md5到服务器判断是否已经存在const res = await api.upPage.execFile({md5: fileMD5});// md5已存在,更新标签if (res) {await api.upPage.updateMD5({md5: fileMD5,name: formState.name,})// 极速秒传成功return true}
}// 3. 切割文件
const getFileList = (file: File): FileList[] => {let cur = 0;const fileChunkList: any[] = [] // blob数组while (cur < file.size) {let chunk = file.slice(cur, cur + BIGSIZE)fileChunkList.push({chunk: chunk,index: fileChunkList.length})cur += BIGSIZE;}return fileChunkList
}// 4. 校验大文件分片上传文件数量
const getExecFileList = async (fileMD5: string): Promise<string | number[]> => {let res = await api.upPage.execFileList({md5: fileMD5});console.log('res', res)return res
}// 5. 开始上传任务
const createUpBigFile = async (fileList: any[], fileMD5: string, fileName: string, fileNumber: number) => {console.log('---fileList', fileList)let promiseList: any[] = []fileList.forEach(async (item, i) => {promiseList.push(up(item.chunk, item.index, fileMD5))})let num = Math.floor(70 / promiseList.length)promiseList.forEach(p => p.then(res => {addText('上传成功1个分片...', num, 'add')return res}))await Promise.all(promiseList)// 5.2 合并文件await mergeFile(fileMD5, fileNumber, fileName, fileList)
}// 5.1 上传
const up = async (file, i, fileMD5) => {let formData = new FormData();formData.append("fileName", fileMD5);formData.append("fileIndex", `${i}`);formData.append("file", file);let res = await api.upPage.saveBigFile(formData);
}// 5.2 合并文件
const mergeFile = async (fileMD5: string, fileNumber: number, fileName: string, fileList: any[]) => {addText('合并文件中...', 90)let res = await api.upPage.mergeFile({fileNumber,md5: fileMD5,name: formState.name,fileName,});console.log('res', res)if (res.type === 'add') {let indexList = res.fileListlet fl: any[] = []fileList.forEach(item => {if (!indexList.includes(item.index) && !indexList.includes(item.index + '')) {fl.push(item)}})createUpBigFile(fl, fileMD5, fileName, fileNumber)}
}
nodejs
const filePath = path.join(__dirname, './static') // 文件上传后保存的路径// 方法函数:添加文件到服务器
const addFileToServer = (file, filePath) => {// 转成文件流var _file = fs.createReadStream(file.filepath)// 存储文件_file.pipe(fs.createWriteStream(filePath))
}// 方法函数:获取文件可读流
const getFile = (path) => {return new Promise(resolve => {const readStream = fs.createReadStream(path, {flags: 'r',encoding: 'base64',});var count = 0;var str = '';readStream.on('data', (data) => { //监听str += datacount++})readStream.on('end', () => { //读取结束resolve(str)})});
}// 返回参数格式化
cosnt sendVal = (val) => return {...,val}/*************************************************************************/
// 校验大文件分片上传文件数量
router.post('/execFileList', async (req, res) => {const data = getReq(req) // 获取传参try {// 文件路径let dirPath = path.join(filePath, data.md5)// 判断是否有该文件夹if (fs.existsSync(dirPath)) {// 列出文件夹中文件名称const filesName = fs.readdirSync(dirPath);return res.send(sendVal(filesName))} else {return res.send(sendVal(false))}} catch (error) {return res.send(sendErr(error))}
})// 大文件上传
router.post('/saveBigFile', async (req, res) => {try {var form = new formidable.IncomingForm();form.encoding = 'utf-8';form.keepExtensions = true;//保留后缀form.parse(req, async (err, fields, files) => {if (err) {return res.send(sendErr(err))}let _filePath = path.join(filePath, fields.fileName)createPath(_filePath) // 如果没有该路径,创建路径// 添加文件到服务器addFileToServer(files.file, `${_filePath}/${fields.fileIndex}`)return res.send(sendVal(1))})} catch (error) {return res.send(sendErr(error))}
})// 大文件合并
router.post('/mergeFile', async (req, res) => {const data = getReq(req) // 获取参数// 字段校验let field = fieldVerification({md5: 'string',name: 'string',fileName: 'string',fileNumber: 'number'}, data)if (field) return res.send(sendErr(3, field))// 登陆权限校验+获取用户信息const userInfo = validateErr(req, "imgUp")if (typeof (userInfo) !== 'object') return res.send(sendErr(userInfo))try {// 获取后缀let extensions = data.fileName.replace(/^.*(\..*?)$/, '$1')// 文件路径let dirPath = path.join(filePath, data.md5)let fileName = path.join(filePath, `${data.md5}${extensions}`)const filesName = fs.readdirSync(dirPath);// 校验文件完整性if (filesName?.length !== data.fileNumber) {return res.send(sendVal({fileList: filesName,type: 'add'}))}// 创建合并流let writeStream = fs.createWriteStream(fileName, {flags: 'w+',encoding: 'base64',})writeStream.on('finish', async () => {console.log('成功');await addFileToSQL(data.md5, data.name, data.fileName, extensions, true)res.send(sendVal({type: 'success'}))})// 合并文件for (let i = 0; i < filesName.length; i++) {let _filePath = path.join(dirPath, `${i}`)// 读取文件let val = await getFile(_filePath)// 写入文件writeStream.write(val)}writeStream.end();} catch (error) {return res.send(sendErr(error))}
})
相关文章:
js文件上传 分片上传/断点续传/极速秒传
(极速秒传)利用md5判断上传的文件是否存在 MD5信息摘要算法,一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。 每一个文件都会生成…...
mysql 通过 binglog 恢复数据
mysql 通过 binglog 恢复数据 测试数据库版本: 8.0.5 查看当前是否开启 进入数据库,查看当前是否开启了 binglog 的相关设置: mysql> show variables like log_bin%; -------------------------------------------------------------- | Variable_name …...
【REST2SQL】01RDB关系型数据库REST初设计
0 概念 REST2SQL实现连接数据库,数据库的表或视图即可提供REST的GET\POST\PUT\DELETE请求,SQL可执行SQLECT\INSERT\UPDATE\DELETE语句。 0.1 RDB Relational Database 即关系型数据库(简称 RDB)是一种以关系(即表格…...
图像识别原理
图像识别是计算机视觉领域中的一个重要任务,其目标是使计算机系统能够理解和解释图像中的信息。以下是图像识别的基本原理: 1. 数据采集:首先,需要获取图像数据。这可以通过摄像头、传感器、扫描仪等设备来实现。图像可以是静态的…...
共识算法介绍
文章目录 共识算法Paxos 算法三种角色一致性提交算法prepare 阶段accept 阶段commit 阶段 CAP 定理BASE 理论Zookeeper 算法实现三类角色三个数据三种模式四种状态消息广播算法Leader选举算法 共识算法 Paxos 算法 Paxos 算法是莱斯利兰伯特(Leslie Lamport)1990 年提出的一种…...
Gen-AI 的知识图和分析(无需图数据库)
如今,图表比以往任何时候都更加相关和有用。由于目前正在发生的人工智能革命,工程师们正在考虑围绕 Gen-AI 的机会,利用具有动态提示、数据基础和屏蔽功能的开放 Gen-AI 解决方案,这进一步促使他们思考知识图谱等有效的解决方案。…...
flutter 安卓使用高德插件黑屏
地址 https://lbs.amap.com/api/android-sdk/guide/create-project/android-studio-create-project 下面介绍的方式是Native配置 sdk,也就是需要手动下载到本地在引入的方式 1、添加 jar 文件: 将下载的地图 SDK 的 jar包复制到工程(此处截…...
Java:表单生成excel文档 poi 通用
在用java 写数据库应用的时候, 通常会生成各种报表,而这些报表可能会被导出为各种格式的文件,比如Excel文档,pdf 文档等等. 今天先做了一个生成Excel 文档的例子,主要解决以下问题: 1. 生成 Excel 文档. 2. 自动对生成…...
使用Apache Commons SCXML实现状态机管理
第1章:引言 大家好,我是小黑,咱们程序员在开发过程中,经常会遇到需要管理不同状态和状态之间转换的场景。比如,一个在线购物的订单,它可能有“新建订单”、“已支付”、“配送中”、“已完成”等状态。在这…...
大数据技术原理与应用期末考试题
大数据技术原理与应用期末考试题 一、单选题 1.下面哪个选项属于大数据技术的“数据存储和管理”技术层面的功能? A、利用分布式文件系统、数据仓库、关系数据库等实现对结构化、半结构化和非结构化海量数据的存储和管理 B、利用分布式并行编程模型和计算框架,结合机器学习…...
解决jenkins的Exec command命令不生效,或者执行停不下来的问题
Jenkins构建完后将war包通过 Publish Over SSH 的插件发布到服务器上,在服务器上执行脚本时,脚本中的 nohup 命令无法执行,并不生效,我配置的Exec command命令是后台启动一个war包,并输出日志文件。 nohup java -jar /…...
【PHP】json_decode的第二个参数是什么意思
json_decode() 函数的第二个参数 $associative 是一个布尔值,用于控制 JSON 对象在 PHP 中的解码方式。当将其设置为 true 时,JSON 对象将被解码为关联数组;当设置为 false 时,JSON 对象将被解码为 stdClass 对象。默认值为 false…...
学生公寓安全用电管理系统应用案例
摘要:安全用电是学校公寓用电管理的首要任务,这就需要对一些恶性负载进行识别和控制,同时为了减少电工和后期管理人员的成本,引进了安全用电管理系统。本文在在描述了安全用电管理系统的工作原理和利用智能电表可实现的功能后,阐明…...
python实现简易的flask后端接口
先安装插件pip install flask 新建py脚本文件编码: # -*- coding: utf-8 -*- from flask import Flask from flask_cors import CORS # 跨域依赖,通过pip install flask-cors安装app Flask(__name__) cors CORS(app) # 跨域设置,这样设置…...
CSDN质量分批量查询
单个文章质量分查询地址(点击右边地址): CSDN质量分查询 创作者身份认证审核标准 优质创作者申请条件: 粉丝数在5000以上近30日(申请日算起)原创文章数不少于4篇原创博文总数不少于100篇垂直领域原创数量…...
【MPC学习笔记】01:MPC简介(Lecture 1_1 Unconstrained MPC)
本笔记来自北航诸兵老师的课程 课程地址:模型预测控制(2022春)lecture 1-1 Unconstrained MPC 文章目录 0 MPC 简介0.1 案例引入0.2 系统模型0.3 MPC的优点0.4 MPC的缺点0.5 MPC的未来 1 详细介绍 0 MPC 简介 0.1 案例引入 MPC(…...
c语言结构体学习上篇
文章目录 前言一、结构体的声明1,什么叫结构体?2,结构体的类型3,结构体变量的创建和初始化4,结构体的类型5,结构体的初始化 二、结构体的访问1,结构体成员的点操作符访问2,结构体体成员的指针访问 前言 昨…...
Linux: eBPF: bcc-tools:tcpdrop使用需要注意的问题
最近使用bcc-tools的时候注意到,bcc-tools(eBPF相关软件)的使用版本和内核的版本紧密程度非常高。因为要使用内核的函数或者结构体,所以就必须版本一致是必须的,不然会出现下面的警告或者错误: WARNING: tcp_drop() kernel function not found or traceable. The kernel …...
AI:113-基于卷积神经网络的图像风格迁移
🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…...
15、Kubernetes核心技术 - 探针
目录 一、概述 二、探针类型 2.1、就绪探针(Readiness Probe) 2.2、存活探针(Liveness Probe) 三、探针探测方法 3.1、exec 3.2、httpGet 3.3、tcpSocket 四、探针配置项 五、探针使用 5.1、就绪探针(Readin…...
GTK4 环境配置
1 安装gtk4包裹: # sudo yum install gtk4 gtk4-devel gtk4-devel-docs devhelp glib2 glib2-devel glib2-doc 2 安装 glade 4 git clone https://github.com/ag-python/cambalache.git 记住 把软件目录 复制到 一个你不会移动删除的地方(千万别删除这个软件文件夹 因为运行…...
Yolov8部署——segmentation部署以及批量推理
Yolov8部署——segmentation部署以及批量推理 参考:在windows上部署Yolov8主要参考下面两个仓库,https://github.com/xunzixunzi/tensorrt-cpp-api和https://github.com/xunzixunzi/YOLOv8-TensorRT-CPP,代码说是适合批量处理,但是代码中是以…...
再见2023,你好2024!
大家好,我是老三,本来今天晚上打算出去转一转,陆家嘴打车实在太艰难了,一公里多的路,司机走了四十分钟,还没到,再加上身体不适,咳嗽地比较厉害,所以还是宅在酒店里&#…...
【计算机毕业设计】SSM二手交易网站
项目介绍 该项目分为前后台,前台普通用户角色,后台管理员角色。 管理员主要功能如下: 登陆,商品分类管理,商品管理,商品订单管理,用户管理等功能。 用户角色主要功能如下: 包含以下功能:查看所有商品,用户登陆注册…...
纠删码ReedSolomon
随着大数据技术的发展,HDFS作为Hadoop的核心模块之一得到了广泛的应用。为了数据的可靠性,HDFS通过多副本机制来保证。在HDFS中的每一份数据都有两个副本,1TB的原始数据需要占用3TB的磁盘空间,存储利用率只有1/3。而且系统中大部分…...
C++音视频开发技巧汇总(持续更新)
1.录制PCM数据 有时候我们需要录制PCM数据到文件以测试录制数据是否正确,一般可以使用以下代码实现: FILE *pf; fopen_s(&pf, "rec.pcm", "wb"); fwrite(myPcmArr, 1, outBufferLen, pf); 录制pcm文件后可以使用Audacity来导…...
4462 4.曙曙献爱心
#include<bits/stdc.h> using namespace std; int n,m,k; int a[1001]; int s[1001]; int f[1001][1001];//f[i][j],i个警察,j个点,能管理的最大人数 int main(){cin>>n>>m>>k;for(int i1;i<n;i){cin>>a[i…...
浅谈命令模式
命令模式是一种行为设计模式,用于将一个请求封装成一个对象,从而使得请求的发送者和接收者解耦,并支持对请求进行参数化、队列化、撤销和重做等操作。 在命令模式中,有一下介个关键角色: Command(命令&am…...
软件测试/测试开发丨Python 模块与包
python 模块与包 python 模块 项目目录结构 组成 package包module模块function方法 模块定义 定义 包含python定义和语句的文件.py文件作为脚本运行 导入模块 import 模块名from <模块名> import <方法 | 变量 | 类>from <模块名> import * 注意&a…...
java企业网站系统Myeclipse开发mysql数据库web结构java编程计算机网页项目
一、源码特点 java Web企业网站系统是一套完善的java web信息管理系统,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发,数据库为Mysql5.0&…...
做网站的挣钱么/seo刷词
作为入门级L2的升级版,NOA(自动辅助导航驾驶,从A点到B点)是近年来不少车企主打的亮点组合功能。同时,通过增加激光雷达的感知冗余,一些车企也在推动从高速场景向城区场景的落地。 按照行业内通用的功能定义…...
滁州网站开发公司/seo策略主要包括
MySql数据库重启服务时报错。在本地计算机 无法启动 MySQL服务。错误1067:进程意外终止有很多情况造成这MySql启动报1067错误常见的有这3种:1、权限设置分配不对,如你运行的mysql用户名没有启动权限,没有读取MySql安装目录和数据库…...
wordpress videopro汉化/网络销售就是忽悠人
WordPress作为外贸建站、风靡全球的网站开源框架,具备移动端自适应、模板资源丰富、功能扩展良好、上手容易等技术特点。除此之外,由于WordPress兼容大量专业级SEO插件,决定了外贸网站优化排名变得相对容易,从而促使WordPress网站…...
什么是小手机型网站/嘉兴网站建设制作
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 …...
军事的网站应如何建设/外链平台有哪些
(*) 窗口的摆放在一个窗口上点右键,有floating, dockable, tabbed document这三种摆放方式。floating: 浮在最上,任何地方。dockable: 浮在最上,任何地方,且可以锚定在一侧。有了dockable之后其实floating就成鸡肋了。tabbed docu…...
山西建设执业注册中心网站/深圳网络营销推广
页面布局中常用的清除浮动的方法 我们在页面布局时,基本上都避免不了使用float,但由此也会引发一些问题,尤其是在容器高度不固定时,此时它的高度完全是由内部的元素撑开的。如果内部元素还是浮动的,那么由于内部的元素…...