vue+node后台处理大文件切片上传--前端部分

ops/2024/9/22 5:59:27/

本文主要介绍,在vue3+vite项目下,如何进行有效的大文件上传,本文章主要讲大文件切片上传方式,并提供简单的demo代码供参考

首先,请确保已经创建好项目,这一步跳过。
1、为了选择合适的文件,我们可以先写一个组件,用来选择文件并且将所选择的文件暴露到app.vue中。
下面是我自己写的组件,可以根据自己的需要进行修改操作。主要的部分有,将默认的拖拽事件,比如进入,悬浮,离开全部阻止默认行为,只允许放置操作,并且在放置操作完成文件内容的读取,代码如下:
FileUpload.vue

javascript"><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中引用组件:

javascript"><FileUpload @getFile="getFile"></FileUpload>

其中涉及到的getfile方法,为子组件调用函数触发的方法,主要用来传递选中的文件:

javascript">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进制字符串,这就是文件的唯一名:

javascript">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当成一个具有一定大小的数组,如果你这么思考,那么下面的代码将通俗易懂,主要做的就是存储切后的块,此外,我们还需要将切片的顺序写入切片名字中,后端可以识别然后进行拼接。

javascript">//切片操作 切成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,否则抛出错误。

javascript">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的范围数据,所以下面的代码有细节,请注意哦,如果你发现这个小小小的细节,请在评论区打出!~

javascript">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、上传文件
到这里,就差最重要的上传文件啦~先来一个按钮,:

javascript"> <button @click="uploadFile">上传</button><el-progress v-for="(item, index) in getpro" :percentage="item" :key="index"/>

所谓文件切片,其实最重要的就是实现文件切片后,可以将切片并行上传,可以极大加快上传的效率,因为充分利用了服务器的吞吐量和效率。所以上述我们已经完成切片,而且将切片的顺序写入切片名字中,后端可以识别然后进行拼接,所以在上传完切片后,还需要发送一个合并文件的请求:

javascript">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啦!


http://www.ppmy.cn/ops/87480.html

相关文章

Python酷库之旅-第三方库Pandas(060)

目录 一、用法精讲 231、pandas.Series.reorder_levels方法 231-1、语法 231-2、参数 231-3、功能 231-4、返回值 231-5、说明 231-6、用法 231-6-1、数据准备 231-6-2、代码示例 231-6-3、结果输出 232、pandas.Series.sort_values方法 232-1、语法 232-2、参数…

精心准备的高水平的博客【点评语】,来抄啊!

大家好&#xff0c;我是一名_全栈_测试开发工程师&#xff0c;已经开源一套【自动化测试框架】和【测试管理平台】&#xff0c;欢迎大家关注我&#xff0c;和我一起【分享测试知识&#xff0c;交流测试技术&#xff0c;趣聊行业热点】。 第 1 条 这篇博客文章如同灯塔般照亮了技…

从入门到自动化:一篇文章掌握Python的80%

Python作为一种高级编程语言&#xff0c;以其简洁明了的语法和强大的功能性&#xff0c;在全球编程社区内享有极高的声誉。本文将带领你从Python的基础语法入手&#xff0c;介绍其常用库的应用&#xff0c;以及如何将Python用于数据分析、网络爬虫和简单的自动化任务&#xff0…

什么是负责任的人工智能

「AI秘籍」系列课程&#xff1a; 人工智能应用数学基础人工智能Python基础人工智能基础核心知识人工智能BI核心知识人工智能CV核心知识AI 进阶&#xff1a;企业项目实战 可直接在橱窗里购买&#xff0c;或者到文末领取优惠后购买&#xff1a; 拥有权利的同时也被赋予了重大的…

Python可视化开发全面教程

Python是一种功能强大且易于学习的编程语言&#xff0c;它还提供了丰富的可视化库&#xff0c;如Matplotlib、Seaborn、Plotly和Bokeh。这些库使得数据可视化变得简单而直观。在本教程中&#xff0c;我们将介绍如何使用Python进行数据可视化&#xff0c;从基础知识到高级技巧。…

crack 基于golang的多并发爆破工具

一款轻巧的内网弱口令枚举工具&#xff0c;项目地址&#xff1a;GitHub - oksbsb/crack: 支持 ftp ssh smb mysql mssql postgres 安装 ​ git clone https://github.com/yanweijin/crack go build main.go基于用法 ➜ crack git:(master) ✗ go run main.go --help …

盘点那些实用的开发技术!!

敏感信息加密操作&#xff0c;让开发的系统更加的安全可靠&#xff01;&#xff01;Jasypt&#xff08;Java Simplified Encryption&#xff09;是一个开源的Java库&#xff0c;用于简化加密操作。https://mp.weixin.qq.com/s/sPBV8Ej46YJsElImodRjAQ每个Web开发都应了解的&…

一款.NET开源、跨平台的DASH/HLS/MSS下载工具

前言 今天大姚给大家分享一款.NET开源&#xff08;MIT License&#xff09;、免费、跨平台的DASH/HLS/MSS下载工具&#xff0c;并且支持点播和直播&#xff08;DASH/HLS&#xff09;的内容下载&#xff1a;N_m3u8DL-RE。 网络流媒体传输协议介绍 DASH DASH是一种基于HTTP的…