vue+node后台处理大文件切片上传--前端部分
本文主要介绍,在vue3+vite项目下,如何进行有效的大文件上传,本文章主要讲大文件切片上传方式,并提供简单的demo代码供参考
首先,请确保已经创建好项目,这一步跳过。
1、为了选择合适的文件,我们可以先写一个组件,用来选择文件并且将所选择的文件暴露到app.vue中。
下面是我自己写的组件,可以根据自己的需要进行修改操作。主要的部分有,将默认的拖拽事件,比如进入,悬浮,离开全部阻止默认行为,只允许放置操作,并且在放置操作完成文件内容的读取,代码如下:
FileUpload.vue
<template><div><div class="upload-demo" ref="up"><el-icon class="el-icon--upload"><upload-filled /></el-icon></div></div>
</template><script setup name="FileUpload">
import { UploadFilled } from '@element-plus/icons-vue'
import { onMounted, reactive, ref } from 'vue'const up = ref(null);
const fileUrl = reactive({});
const emit = defineEmits(['getFile']);
onMounted(() => {console.log(up.value)up.value.addEventListener('dragenter', (e)=>{ //阻止默认事件e.preventDefault();e.stopPropagation();})up.value.addEventListener('dragover', (e)=>{e.preventDefault();e.stopPropagation();})up.value.addEventListener('dragleave', (e)=>{e.preventDefault();e.stopPropagation();})up.value.addEventListener('drop', async (e)=>{e.preventDefault();e.stopPropagation();const f = e.dataTransfer.files; //在此处获取用户拖拽进去组件的文件数组const file = f[0]; //取第一个,在这里我默认只拖拽一个,可以自己选择性进行扩展,如果怕f为空报错,选择trycatch,我为了方便就没写if(!file) return console.error('没有文件')if(file.size > 1024*1024*1024*2) return console.error('文件太大'); //文件超过2G,太大不上传if(!file.type.startsWith('video')) return console.error('只能上传视频文件'); Object.assign(fileUrl, file) emit('getFile', file);})
})
defineExpose({fileUrl})
</script><style scoped>
.upload-demo{width: 200px;height: 100px;border: 1px dotted gray;transition: all 3s;font-size: 100px;display: flex;justify-content: center;align-items: center;
}
.upload-demo:hover{border: 1px dotted blue
}
</style>
在app.vue中引用组件:
<FileUpload @getFile="getFile"></FileUpload>
其中涉及到的getfile方法,为子组件调用函数触发的方法,主要用来传递选中的文件:
let url = ref('');
let upfile = reactive<{[key: string]: string}>({});
let appfile:any = null;
function getFile(file:any){console.log('child set:', file);console.log(typeof file); //File类型,根据原型链,底层是objectif(typeof file != 'object'){return console.error('文件格式不对,非对象');}url.value = URL.createObjectURL(file); //创建临时URL地址,索引到用户本地的文件地址,方便页面展示appfile = file;console.log(appfile);Object.assign(upfile, { //创建一个包含文件地址,文件名,类型的对象进行保存url,type: file.type,name: file.name})
}
子组件到这里就结束,我将主体逻辑写到app中,方便展示,最好的方式还是全部封装到子组件,达到高内聚低耦合,每一个模块只干一件事。
2、获取唯一的文件名称
通常,对于每一个相同的文件,我们需要为其生成一个唯一的名字,然后上传到服务器,根据唯一名称,判断是否同一个文件,节省资源的同时,提高用户体验。如何根据文件内容获取唯一的文件名称,我们先将文件内容转为二进制,再利用浏览器提高的api:crypto.subtle.digest生成哈希值(注意,该方法异步,容易被忽略),再转为16进制字符串,这就是文件的唯一名:
async function getFilename(){const hashname = await getFileHashname();const fileExtension = upfile.name.split('.').pop();return `${hashname}.${fileExtension}`;
}
async function getFileHashname(){console.log(appfile);const arrayBuffer = await appfile.arrayBuffer();const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer);const hex = Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join('');return hex;
}
3、切片操作
首先认识一下文件大小的概念,浓缩为以下公式:
1GB = 1024MB = 1024*1024KB = 1024*1024*1024B
那么文件切片,也就是将很大的文件切成很多块,每块的大小我们自己决定,当然,在现实应用中,是需要根据网络流量,服务器的负载等因素进行动态调整这个切片的大小,我们切片的过程中,完全可以把File当成一个具有一定大小的数组,如果你这么思考,那么下面的代码将通俗易懂,主要做的就是存储切后的块,此外,我们还需要将切片的顺序写入切片名字中,后端可以识别然后进行拼接。
//切片操作 切成10*1024*1024,也就是10MB
const CHUNKS_SIZE = 10 * 1024 * 1024;
async function sectionFile(filename:string){console.log(filename);const chunks = createChunks(filename);console.log(chunks);return chunks;
}
function createChunks(filename: string){let chunks = [];const count = Math.ceil(appfile.size / CHUNKS_SIZE);for(let i = 0; i < count; i++){let chunk = appfile.slice(i*CHUNKS_SIZE, (i+1)*CHUNKS_SIZE);chunks.push({chunk,chunkFileName: `${filename}-${i}`})}return chunks;
}
3、封装axios
这一步主要做的操作是提前封装一下axios,更加好的适应后端返回的主体内容,当然,你可以选择不进行这一步,因为这不是必须的。
简单封装axios,判断是否返回的主体内容有success,否则抛出错误。
import axios from 'axios';const axiosInstance = axios.create({baseURL: 'http://localhost:8080'
})
axiosInstance.interceptors.response.use((response)=>{ if(response.data && response.data.success){return response.data;} else {return new Error(response.data.msssage || 'service error')}},(error)=>{console.log(error);return error;
})
4、上传进度条
一开始小伙伴肯定很好奇,为什么我发送的请求看不见进度,但是别人怎么看见进度的,难道后端有返回参数嘛,当然不是!如果要一直返回参数,那这个开销可就不得了啦! 其实事情是这样子的,axios请求提供一个方法,来监听上传的进度,也就是请求的进度,如果你能够熟练使用这个方法,那么对页面的一些交互大有裨益!这个方法就是onUploadProgress! 其中参数是progressEvent,有两个重要属性,total和loaded,意义如同属性名:但是值得注意的是:loaded是一个0~1的范围数据,所以下面的代码有细节,请注意哦,如果你发现这个小小小的细节,请在评论区打出!~
let progress = reactive<{[key: string]: number}>({});
onUploadProgress: (progressEvent)=>{if(!progressEvent.total) return ;const percentProgress = Math.round(progressEvent.loaded*100 / progressEvent.total)Object.assign(progress, {...progress, [item.chunkFileName]: percentProgress})console.log("shangchuangzhong");}
5、上传文件
到这里,就差最重要的上传文件啦~先来一个按钮,:
<button @click="uploadFile">上传</button><el-progress v-for="(item, index) in getpro" :percentage="item" :key="index"/>
所谓文件切片,其实最重要的就是实现文件切片后,可以将切片并行上传,可以极大加快上传的效率,因为充分利用了服务器的吞吐量和效率。所以上述我们已经完成切片,而且将切片的顺序写入切片名字中,后端可以识别然后进行拼接,所以在上传完切片后,还需要发送一个合并文件的请求:
async function uploadFile(){const filename = await getFilename();console.log('name: ', filename);const chunks = await sectionFile(filename);const requsetList = chunks.map(item => {return axiosInstance.post(`/upload/${filename}`, item.chunk, {headers:{'Content-Type':'application/octet-stream'},params: {chunkFileName: item.chunkFileName},onUploadProgress: (progressEvent)=>{if(!progressEvent.total) return ;const percentProgress = Math.round(progressEvent.loaded*100 / progressEvent.total)Object.assign(progress, {...progress, [item.chunkFileName]: percentProgress})console.log("shangchuangzhong");}})})try {await Promise.all(requsetList);//切片发送完还需要发一个合并文件内容的请求await axiosInstance.get(`/mergeFile/${filename}`, {headers: {'Content-Type': 'application/json'}}).then(()=>{console.log('rollback');// Object.assign(progress, null) //没法做到清空对象Object.keys(progress).forEach(key => {delete progress[key];});})console.log('上传完毕!')} catch (error) {console.log(error);return ;}
}
到这里就ok啦!
相关文章:
vue+node后台处理大文件切片上传--前端部分
本文主要介绍,在vue3vite项目下,如何进行有效的大文件上传,本文章主要讲大文件切片上传方式,并提供简单的demo代码供参考 首先,请确保已经创建好项目,这一步跳过。 1、为了选择合适的文件,我们…...
【通俗理解】艺术与数学交融
【通俗理解】艺术与数学交融 艺术与数学的奇妙交融 你可以把艺术比作一个“梦幻花园”,它充满了无限的可能性和美感。而数学则是一把“精密钥匙”,它能够解开花园中的秘密,揭示美的内在结构。 艺术与数学交融的核心作用 组件/步骤描述艺术表…...
深入探讨 Docker 容器文件系统
引言 随着云计算和微服务架构的兴起,Docker 容器技术迅速成为开发和运维人员的首选工具。Docker 容器不仅提供了一种轻量级的虚拟化方式,还简化了应用程序的部署和管理。在众多的技术细节中,Docker 容器文件系统是一个至关重要的组成部分。本…...
《LeetCode热题100》---<4.子串篇三道>
本篇博客讲解LeetCode热题100道子串篇中的三道题 第一道:和为 K 的子数组 第二道:滑动窗口最大值 第三道:最小覆盖子串 第一道:和为 K 的子数组(中等) 法一:暴力枚举 class Solution {public in…...
全国区块链职业技能大赛样题第9套前端源码
后端源码地址:https://blog.csdn.net/Qhx20040819/article/details/140746050 前端源码地址:https://blog.csdn.net/Qhx20040819/article/details/140746216 智能合约+数据库表设计:https://blog.csdn.net/Qhx20040819/article/details/140746646 登录 用户管理...
如何提高编程面试成功率:LeetCode Top 100 问题及解答解析(详细面试宝典)
以下是 LeetCode Top 100 面试必备题目及其解决方案示例。这些题目涵盖了数据结构、算法、动态规划、回溯等多种重要的面试话题。希望各位同学有所收货,早日脱离底层到达彼岸! 1. Two Sum 题目: 给定一个整数数组 nums 和一个目标值 target,…...
K-近邻和神经网络
K-近邻(K-NN, K-Nearest Neighbors) 原理 K-近邻(K-NN)是一种非参数分类和回归算法。K-NN 的主要思想是根据距离度量(如欧氏距离)找到训练数据集中与待预测样本最近的 K 个样本,并根据这 K 个…...
用EasyV全景图低成本重现真实场景,360°感受数字孪生
全景图,即借助绘画、相片、视频、三维模型等形式,通过广角的表现手段,尽可能多表现出周围的环境。避免了一般平面效果图视角单一,不能带来全方位视角的缺陷,能够全方位的展示360度球型范围内的所有景致,最大…...
【Golang 面试 - 进阶题】每日 3 题(九)
✍个人博客:Pandaconda-CSDN博客 📣专栏地址:http://t.csdnimg.cn/UWz06 📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞👍收藏…...
孟德尔随机化、R语言,报错,如何解决?
🏆本文收录于《CSDN问答解惑-专业版》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收…...
一文剖析高可用向量数据库的本质
面对因电力故障、网络问题或人为操作失误等导致的服务中断,数据库系统高可用能够保证系统在这些情况下仍然不间断地提供服务。如果数据库系统不具备高可用性,那么系统就需要承担停机和数据丢失等重大风险,而这些风险极有可能造成用户流失&…...
JavaScript青少年简明教程:异常处理
JavaScript青少年简明教程:异常处理 在 JavaScript 中,异常指的是程序执行过程中出现的错误或异常情况。这些错误可能导致程序无法正常执行,甚至崩溃。ECMA-262规范了多种JavaScript错误类型,这些类型都继承自Error基类。主要的错…...
科普文:Lombok使用及工作原理详解
1. 概叙 Lombok是什么? Project Lombok 是一个 JAVA 库,它可以自动插入编辑器和构建工具,为您的 JAVA 锦上添花。再也不要写另一个 getter/setter 或 equals 等方法,只要有一个注注解,你的类就有一个功能齐全的生成器…...
飞致云开源社区月度动态报告(2024年7月)
自2023年6月起,中国领先的开源软件公司FIT2CLOUD飞致云以月度为单位发布《飞致云开源社区月度动态报告》,旨在向广大社区用户同步飞致云旗下系列开源软件的发展情况,以及当月主要的产品新版本发布、社区运营成果等相关信息。 飞致云开源大屏…...
mybatis-plus——实现动态字段排序,根据实体获取字段映射数据库的具体字段
前言 前端需要根据表头的点击控件可以排序,虽然前端能根据当前页的数据进行对应字段的排序,但也仅局限于实现当前页的排序,无法满足全部数据的排序,所以需要走接口的查询进行排序,获取最全的排序数据 实现方案 前端…...
redis:Linux安装redis,redis常用的数据类型及相关命令
1. 什么是NoSQL nosql[not only sql]不仅仅是sql。所有非关系型数据库的统称。除去关系型数据库之外的都是非关系数据库。 1.1为什么使用NoSQL NoSQL数据库相较于传统关系型数据库具有灵活性、可扩展性和高性能等优势,适合处理非结构化和半结构化数据,…...
JavaScript 和 HTML5 Canvas实现图像绘制与处理
前言 JavaScript 和 HTML5 的 canvas 元素提供了强大的图形和图像处理功能,使得开发者能够在网页上创建动态和交互式的视觉体验。这里我们将探讨如何使用 canvas 和 JavaScript 来处理图像加载,并在其上进行图像绘制。我们将实现一个简单的示例…...
Java之Java基础二十(集合[上])
Java 集合框架可以分为两条大的支线: ①、Collection,主要由 List、Set、Queue 组成: List 代表有序、可重复的集合,典型代表就是封装了动态数组的 ArrayList 和封装了链表的 LinkedList;Set 代表无序、不可重复的集…...
【C++BFS】1162. 地图分析
本文涉及知识点 CBFS算法 LeetCode1162. 地图分析 你现在手里有一份大小为 n x n 的 网格 grid,上面的每个 单元格 都用 0 和 1 标记好了。其中 0 代表海洋,1 代表陆地。 请你找出一个海洋单元格,这个海洋单元格到离它最近的陆地单元格的距…...
实战:安装ElasticSearch 和常用操作命令
概叙 科普文:深入理解ElasticSearch体系结构-CSDN博客 Elasticsearch各版本比较 ElasticSearch 单点安装 1 创建普通用户 #1 创建普通用户名,密码 [roothlink1 lyz]# useradd lyz [roothlink1 lyz]# passwd lyz#2 然后 关闭xshell 重新登录 ip 地址…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...
Unity UGUI Button事件流程
场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...
