使用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怎么实现一个大文件分片上传功能的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注恰卡编程网行业资讯频道,感谢各位的阅读!
推荐阅读
-
node.js中Buffer对象有什么用
这篇文章将为大家详细讲解有关node.js中Buffer对象有什么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇...
-
Node.js实现断点续传
Node.js实现断点续传目录方案分析切片断点续传具体解决流程逻辑分析前端服务端小结方案分析切片就是对上...
-
使用Node.js怎么实现一个HTTP服务器
这篇文章将为大家详细讲解有关使用Node.js怎么实现一个HTTP服务器,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅...
-
jade模板引擎如何在Node.js中使用
jade模板引擎如何在Node.js中使用?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章...
-
如何在Node.js中引入UIBootstrap
本篇文章给大家分享的是有关如何在Node.js中引入UIBootstrap,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这...
-
Angular如何在Node.js中使用
本篇文章为大家展示了Angular如何在Node.js中使用,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍...
-
AngularJS怎么在Node.js中使用
本篇文章给大家分享的是有关AngularJS怎么在Node.js中使用,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文...
-
使用node.js如何连接mysql
使用node.js如何连接mysql?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人...
-
body-parser怎么在node.js中使用
本篇文章为大家展示了body-parser怎么在node.js中使用,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的...
-
如何在node.js中使用Express服务器
这篇文章给大家介绍如何在node.js中使用Express服务器,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助...
