使用Node.js怎么实现一个大文件分片上传功能

使用Node.js怎么实现一个大文件分片上传功能?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

前端

使用Node.js怎么实现一个大文件分片上传功能

1. index.html

<!DOCTYPEhtml>
<htmllang="en">
<head>
<metacharset="UTF-8">
<metaname="viewport"content="width=device-width,initial-scale=1.0">
<metahttp-equiv="X-UA-Compatible"content="ie=edge">
<title>文件上传</title>

<scriptsrc="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script>
<scriptsrc="https://code.jquery.com/jquery-3.4.1.js"></script>
<scriptsrc="./spark-md5.min.js"></script>

<script>

$(document).ready(()=>{
constchunkSize=1*1024*1024;//每个chunk的大小,设置为1兆
//使用Blob.slice方法来对文件进行分割。
//同时该方法在不同的浏览器使用方式不同。
constblobSlice=
File.prototype.slice||File.prototype.mozSlice||File.prototype.webkitSlice;

consthashFile=(file)=>{
returnnewPromise((resolve,reject)=>{

constchunks=Math.ceil(file.size/chunkSize);
letcurrentChunk=0;
constspark=newSparkMD5.ArrayBuffer();
constfileReader=newFileReader();
functionloadNext(){
conststart=currentChunk*chunkSize;
constend=start+chunkSize>=file.size?file.size:start+chunkSize;
fileReader.readAsArrayBuffer(blobSlice.call(file,start,end));
}
fileReader.onload=e=>{
spark.append(e.target.result);//Appendarraybuffer
currentChunk+=1;
if(currentChunk<chunks){
loadNext();
}else{
console.log('finishedloading');
constresult=spark.end();
//如果单纯的使用result作为hash值的时候,如果文件内容相同,而名称不同的时候
//想保留两个文件无法保留。所以把文件名称加上。
constsparkMd5=newSparkMD5();
sparkMd5.append(result);
sparkMd5.append(file.name);
consthexHash=sparkMd5.end();
resolve(hexHash);
}
};
fileReader.onerror=()=>{
console.warn('文件读取失败!');
};
loadNext();
}).catch(err=>{
console.log(err);
});
}

constsubmitBtn=$('#submitBtn');
submitBtn.on('click',async()=>{
constfileDom=$('#file')[0];
//获取到的files为一个File对象数组,如果允许多选的时候,文件为多个
constfiles=fileDom.files;
constfile=files[0];
if(!file){
alert('没有获取文件');
return;
}
constblockCount=Math.ceil(file.size/chunkSize);//分片总数
constaxiosPromiseArray=[];//axiosPromise数组
consthash=awaithashFile(file);//文件hash
//获取文件hash之后,如果需要做断点续传,可以根据hash值去后台进行校验。
//看看是否已经上传过该文件,并且是否已经传送完成以及已经上传的切片。
console.log(hash);

for(leti=0;i<blockCount;i++){
conststart=i*chunkSize;
constend=Math.min(file.size,start+chunkSize);
//构建表单
constform=newFormData();
form.append('file',blobSlice.call(file,start,end));
form.append('name',file.name);
form.append('total',blockCount);
form.append('index',i);
form.append('size',file.size);
form.append('hash',hash);
//ajax提交分片,此时content-type为multipart/form-data
constaxiosOptions={
onUploadProgress:e=>{
//处理上传的进度
console.log(blockCount,i,e,file);
},
};
//加入到Promise数组中
axiosPromiseArray.push(axios.post('/file/upload',form,axiosOptions));
}
//所有分片上传后,请求合并分片文件
awaitaxios.all(axiosPromiseArray).then(()=>{
//合并chunks
constdata={
size:file.size,
name:file.name,
total:blockCount,
hash
};
axios
.post('/file/merge_chunks',data)
.then(res=>{
console.log('上传成功');
console.log(res.data,file);
alert('上传成功');
})
.catch(err=>{
console.log(err);
});
});
});

})

window.onload=()=>{
}

</script>

</head>
<body>
<h2>大文件上传测试</h2>
<section>
<h4>自定义上传文件</h4>
<inputid="file"type="file"name="avatar"/>
<div>
<inputid="submitBtn"type="button"value="提交">
</div>
</section>

</body>
</html>

2. 依赖的文件axios.js jquery spark-md5.js

后端

1. app.js

constKoa=require('koa');
constapp=newKoa();
constRouter=require('koa-router');
constmulter=require('koa-multer');
constserve=require('koa-static');
constpath=require('path');
constfs=require('fs-extra');
constkoaBody=require('koa-body');
const{mkdirsSync}=require('./utils/dir');
constuploadPath=path.join(__dirname,'uploads');
constuploadTempPath=path.join(uploadPath,'temp');
constupload=multer({dest:uploadTempPath});
constrouter=newRouter();
app.use(koaBody());
/**
*single(fieldname)
*Acceptasinglefilewiththenamefieldname.Thesinglefilewillbestoredinreq.file.
*/
router.post('/file/upload',upload.single('file'),async(ctx,next)=>{
console.log('fileupload...')
//根据文件hash创建文件夹,把默认上传的文件移动当前hash文件夹下。方便后续文件合并。
const{
name,
total,
index,
size,
hash
}=ctx.req.body;

constchunksPath=path.join(uploadPath,hash,'/');
if(!fs.existsSync(chunksPath))mkdirsSync(chunksPath);
fs.renameSync(ctx.req.file.path,chunksPath+hash+'-'+index);
ctx.status=200;
ctx.res.end('Success');
})

router.post('/file/merge_chunks',async(ctx,next)=>{
const{
size,name,total,hash
}=ctx.request.body;
//根据hash值,获取分片文件。
//创建存储文件
//合并
constchunksPath=path.join(uploadPath,hash,'/');
constfilePath=path.join(uploadPath,name);
//读取所有的chunks文件名存放在数组中
constchunks=fs.readdirSync(chunksPath);
//创建存储文件
fs.writeFileSync(filePath,'');
if(chunks.length!==total||chunks.length===0){
ctx.status=200;
ctx.res.end('切片文件数量不符合');
return;
}
for(leti=0;i<total;i++){
//追加写入到文件中
fs.appendFileSync(filePath,fs.readFileSync(chunksPath+hash+'-'+i));
//删除本次使用的chunk
fs.unlinkSync(chunksPath+hash+'-'+i);
}
fs.rmdirSync(chunksPath);
//文件合并成功,可以把文件信息进行入库。
ctx.status=200;
ctx.res.end('合并成功');
})
app.use(router.routes());
app.use(router.allowedMethods());
app.use(serve(__dirname+'/static'));
app.listen(9000);

2. utils/dir.js

constpath=require('path');
constfs=require('fs-extra');
constmkdirsSync=(dirname)=>{
if(fs.existsSync(dirname)){
returntrue;
}else{
if(mkdirsSync(path.dirname(dirname))){
fs.mkdirSync(dirname);
returntrue;
}
}
}
module.exports={
mkdirsSync
};

操作步骤说明

服务端的搭建

我们以下的操作都是保证在已经安装node以及npm的前提下进行。node的安装以及使用可以参考官方网站。

1、新建项目文件夹file-upload

2、使用npm初始化一个项目:cd file-upload && npm init

3、安装相关依赖

npmikoa
npmikoa-router--save//Koa路由
npmikoa-multer--save//文件上传处理模块
npmikoa-static--save//Koa静态资源处理模块
npmifs-extra--save//文件处理
npmikoa-body--save//请求参数解析

4、创建项目结构

file-upload
-static
-index.html
-spark-md5.min.js
-uploads
-temp
-utils
-dir.js
-app.js

5、复制相应的代码到指定位置即可

6、项目启动:node app.js (可以使用 nodemon 来对服务进行管理)

7、访问:http://localhost:9000/index.html

看完上述内容,你们掌握使用Node.js怎么实现一个大文件分片上传功能的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注恰卡编程网行业资讯频道,感谢各位的阅读!

发布于 2021-04-15 01:55:35
收藏
分享
海报
0 条评论
186
上一篇:使用python怎么绘制一个双y轴图像 下一篇:使用django框架怎么一次性上传多个文件
目录

    0 条评论

    本站已关闭游客评论,请登录或者注册后再评论吧~

    忘记密码?

    图形验证码