vue+node后台处理大文件切片上传--前端部分
创始人
2024-11-15 04:33:34
0

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

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

     

在app.vue中引用组件:

 

其中涉及到的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类型,根据原型链,底层是object   if(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、上传文件
到这里,就差最重要的上传文件啦~先来一个按钮,:

    

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

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

相关内容

热门资讯

透视app!aapoker怎么... 透视app!aapoker怎么设置抽水(透视)可以开辅助器(一贯是真的有挂)一、aapoker怎么设...
透视教程!哈糖大菠萝软件下载,... 透视教程!哈糖大菠萝软件下载,wepoker脚本(透视)原先有挂(科技教程)透视教程!哈糖大菠萝软件...
透视总结(WPK)确实有挂(透... 透视总结(WPK)确实有挂(透视)wpk辅助软件(攻略方法);1、wpk辅助软件透视辅助简单,wpk...
透视挂!pokernow辅助工... 透视挂!pokernow辅助工具,哈糖大菠萝怎么挂,真是是真的有挂(攻略教程)1、任何哈糖大菠萝怎么...
透视辅助!aapoker辅助工... 透视辅助!aapoker辅助工具安全吗(透视)发牌逻辑(总是有挂)1、超多福利:超高返利,海量正版游...
透视私人局!hh poker插... 透视私人局!hh poker插件下载,约局吧德州真的有透视挂吗(透视)起初存在有挂(技巧教程)1、构...
透视透视(WPK)切实真的有挂... 透视透视(WPK)切实真的有挂(透视)wpk有作弊吗(攻略方法)1)wpk有作弊吗辅助挂:进一步探索...
透视攻略!epoker有透视吗... 透视攻略!epoker有透视吗,拱趴大菠萝机器人,其实有挂(新2025教程);1、下载好拱趴大菠萝机...
透视科技!aapoker公共底... 透视科技!aapoker公共底牌(透视)辅助器是真的(竟然是有挂)1、每一步都需要思考,不同水平的挑...
透视好友!wepoker有没有... 透视好友!wepoker有没有挂,hhpoker辅助软件(透视)起初是真的有挂(解密教程)1、hhp...