当前位置: 首页 > Web前端 > HTML

Node.js实现大文件断点续传

时间:2023-03-28 14:24:06 HTML

前言正常的业务需求:上传图片、Excel等,毕竟几M的大小也能快速上传到服务器。上传几百M、几G的视频等大文件,需要等待很长时间。这创建了相应的解决方案。对于大文件上传出现卡顿、断线、网络状况不佳的情况,使用分片+断点续传可以很好的应对以上情况。方案分析分片是对上传的视频进行分片,具体操作是:File.slice(start,end):返回一个新的blob对象复制blob的起始字节复制blob的结束字节读取的个数上传同一文件的切片。如果有新文件上传,服务器返回0,否则返回上传的切片数。具体解决过程本demo提供了关键思路和方法,其他功能如:文件限制、lastModifiedDate检查文件可重复性、定时清除缓存文件等功能扩展可在此代码基础上进行。html部分submitscript部分让我们计数=0;//记录需要上传的文件的下标consthandleVideo=async(event,name,url)=>{//阻止浏览器的默认表单事件event.preventDefault();letcurrentSize=document.querySelector("h2");letfiles=document.querySelector(name).files;//默认的分片个数constsectionLength=100;//先请求接口获取文件在服务器上是否存在//如果计数为0,为第一次上传,计数不为0,则文件存在于服务器,返回上传的分片数count=awaithandleCancel(files[0]);//声明数组对象用于存储切片letfileCurrent=[];//循环文件对象for(constfileof[...files]){//获取每个切片的大小letitemSize=Math.ceil(file.size/sectionLength);//循环文件大小,文件blob存储在数组中letcurrent=0;for(current;current90){source.cancel("取消请求");}//保存Enter文件相关信息//file为切片blob对象//filename为文件名//index为当前切片数//total为切片总数letformData=newFormData();formData.append("文件",item.file);formData.append("文件名",文件名);formData.append("总计",sectionLength);formData.append("index",index+count+1);awaitaxios({url:`http://localhost:8080/${url}`,method:"POST",data:formData,cancelToken:source.token,}).then((response)=>{//返回数据显示进度currentSize.innerHTML=`progress${response.data.size}%`;}).catch((err)=>{console.log(err);});}}};//请求接口,查询上传的文件是否存在//如果计数为0,则表示不存在,如果计数不为0,则对应的切片数已经上传consthandleCancel=(file)=>{returnaxios({method:"post",url:"http://localhost:8080/getSize",headers:{"Content-Type":"application/json;charset=utf-8"},数据:{文件名:文件名,},}).然后((res)=>{returnres.data.count;}).catch((err)=>{console.log(err);});};nodeserver部分//使用express搭建服务器apiconstexpress=require("express");//引入上传文件逻辑代码constupload=require("./upload_file");//处理所有响应,设置跨域app.all("*",(req,res,next)=>{res.header("Access-Control-Allow-Origin","*");res.header("Access-Control-Allow-Headers","X-Requested-With");res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");res.header("Access-Control-Allow-Headers","内容类型,X-Requested-With");res.header("X-Powered-By","3.2.1");水库header("Content-Type","application/json;charset=utf-8");下一个();});constapp=express();app.use(bodyParser.json({type:"application/*+json"}));//视频上传(查询当前切片数)app.post("/getSize",上传.getSize);//视频上传接口app.post("/video",upload.video);//开启本地端口监听app.listen(8080);upload_file//文件上传模块constformidable=require("formidable");//文件系统模块constfs=require("fs");//系统路径模块constpath=require("path");//操作写文件流consthandleStream=(item,writeStream)=>{//读取Get对应目录文件bufferconstreadFile=fs.readFileSync(item);//写入读取缓冲区||分块到流writeStream.write(readFile);//写入后清空暂存的切片文件fs.unlink(item,()=>{});};//视频上传(切片)module.exports.video=(req,res)=>{//创建解析对象constform=newformidable.IncomingForm();//设置视频文件上传路径letdirPath=path.join(__dirname,"video");佛rm.uploadDir=dirPath;//是否保留上传文件名后缀form.keepExtensions=true;//errerror对象包含错误信息,如果解析失败//fields包含除二进制之外的formData的键值对象//文件对象类型上传文件的信息form.parse(req,async(err,fields,file)=>{//获取上传的文件blob对象letfiles=file.file;//获取当前切片索引letindex=fields.index;//获取切片总数lettotal=fields.total;//获取文件nameletfilename=fields.filename;//重写上传文件名并设置暂存目录leturl=dirPath+"/"+filename.split(".")[0]+`_${index}.`+filename.split(".")[1];try{//同步修改上传的文件名fs.renameSync(files.path,url);console.log(url);//异步处理setTimeout(()=>{//判断最后一片是否上传完毕,拼接写入所有视频if(index===total){//同步新建一个目录存放完整视频letnewDir=__dirname+`/uploadFiles/${Date.现在()}`;//创建目录fs.mkdirSync(newDir);//创建一个用于写入文件的可写流letwriteStream=fs.createWriteStream(newDir+`/${文件名}`);让fsList=[];//取出所有分片文件放入数组中for(leti=0;i{letcount=0;req.setEncoding("utf8");req.on("data",function(data){letname=JSON.parse(data);letdirPath=path.join(__dirname,"video");//计算上传的片段分片文件数letfiles=fs.readdirSync(dirPath);files.forEach((item,index)=>{leturl=name.fileName.split(".")[0]+`_${index+1}.`+name.fileName.split(".")[1];如果(files.includes(url)){++count;}});res.send({code:0,msg:"请继续上传",count,});});};详见前端高级面试题逻辑分析前端先请求上传,检查文件是否是第一次上传,或者第一次上传有对应的分片文件,那么切片是从如果文件中已经有对应的切片从0开始,请求上传从切片数开始的循环切片数组,上传每个切片文件,这里使用的是模拟手动暂停请求。当分片数大于90时,取消请求服务器接收查询文件filename并查找暂存文件地址,判断是否有对应的未上传过的上传文件,返回0,数量ofslice从0开始已经上传,然后返回相应的slice数量接收上传的文件切片,通过Count和total判断是否上传文件到暂存目录。上传完成后,创建文件存储目录,创建可写流,执行写操作,提取对应的临时文件放入数组,循环遍历文件目录数组,依次读写文件缓冲区.写完,关闭可写流总结上面的代码涉及到具体的业务流程会发生变化或偏离,这只是其中的一种具体实现方式。希望这篇文章能对大家有所帮助。如果有不对的地方,还望指点。