node.js express框架实现文件上传与下载功能实例详解
本文实例讲述了node.jsexpress框架实现文件上传与下载功能。分享给大家供大家参考,具体如下:
背景
昨天吉视传媒的客户对IPS信息发布系统又提了一个新需求,就是发布端发送消息时需要支持附件的上传,而接收端可以对发布端上传的附件进行下载;接收端回复消息时也需要支持上传附件,发布端可以对所有接收端上传的附件进行打包下载。
功能实现
- 前台部分
前台使用webUploader插件即可,这是百度开发的一款文件上传组件,具体使用查看它的API即可。这个项目之前开发的时候前台使用了angular.js。
$scope.fileName=""; //创建上传附件的对象 var$list=$("#thelist"); varuploader=WebUploader.create({ //选完文件后,是否自动上传。 auto:false, //swf文件路径 swf:'../../../lib/webUploader/Uploader.swf', //文件接收服务端。 server:'/publishUploadFile', //内部根据当前运行是创建,可能是input元素,也可能是flash. pick:{ id:'#filePicker', //只能选择一个文件上传 multiple:false }, //pick:'#filePicker', method:'POST', }); uploader.on('fileQueued',function(file){ $scope.fileName=file.name; $list.html(""); $list.html(file.name); });
当用户选择文件的时候我创建了文件上传的对象,而在用户真正发送消息的时候我添加了相应的参数并将附件真正的上传上去,符合我这个项目的业务逻辑。
if($scope.fileName){ //添加参数 uploader.options.formData.fileId=fileId; uploader.options.formData.fileName=$scope.fileName; uploader.upload(); }
- 后台部分
路由就不详细说明了,主要注意的是下载的接口我都是使用的get请求,这样前台在请求的时候直接新打开一个窗口拼接了相应的参数就能下载文件了。下面贴一下action层的代码:
//发布端上传附件 exports.publishUploadFile=function(req,res){ messageMng.publishUploadFile(req,function(err,datas){ res.json(datas); }); }; //下载发布端上传的附件 exports.exportPublishFile=function(req,res){ messageMng.exportPublishFile(req,function(err,datas){ if(err){ res.set({ "Content-Disposition":"attachment;filename="+encodeURI("error.txt") }); res.write(err.message); res.end(); }else{ res.download(datas.path,encodeURI(datas.name)); } }); }; //接收端上传附件 exports.uploadFile=function(req,res){ messageMng.uploadFile(req,function(err,datas){ res.json(datas); }); }; //发布端导出附件 exports.exportFile=function(req,res){ messageMng.exportFile(req,function(err,datas){ if(err){ res.set({ "Content-Disposition":"attachment;filename="+encodeURI("error.txt") }); res.write(err.message); res.end(); }else{ //第一种方式下载完的zip解压报错 //res.download(datas.path,datas.name+".zip"); //第二种方式 //varpath="D:/maven介绍.ppt"; varf=fs.createReadStream(datas.path); res.writeHead(200,{ 'Content-Type':'application/force-download', 'Content-Disposition':'attachment;filename='+encodeURI(datas.name)+'.zip' }); f.pipe(res); } }); };
这里着重说一下下载zip时使用download下载完的压缩包解压会报错,使用第二种方法完美解决。
然后是service层的代码:
/** *发布端上传附件 *@paramreq *@paramfn */ MessageManager.prototype.publishUploadFile=function(req,fn){ try{ //消息ID varfileId=req.body.fileId; varfile=req.file; //文件上传的目录 varuploadFolder=path.join(__dirname,'../../upload/publishUploadFile/'+fileId); //判断文件夹是否存在不存在则创建 toolUtil.mkdirSync(uploadFolder); //将上传的文件从临时目录拷贝到指定的目录下 varfileReadStream=fs.createReadStream(file.path); varfileWriteStream=fs.createWriteStream(uploadFolder+"/"+file.originalname); fileReadStream.pipe(fileWriteStream); fileWriteStream.on('close',function(){ //删除临时目录下面的文件 toolUtil.emptyDir(file.destination); }); fn(null,{"data":"","message":"上传成功","error_code":200}); }catch(e){ fn(e,{"data":"","message":"上传失败","error_code":e.message}); } }; /** *下载发布端上传的附件 *@paramreq *@paramfn */ MessageManager.prototype.exportPublishFile=function(req,fn){ try{ //附件ID varid=req.query.id; //附件名称或标题 varname=req.query.name; if(id&&name){ //名称过长的话,截取前25个字符 if(name.length>25){ name=name.substr(0,24); } //将要压缩得文件夹路径 varfilePath=path.join(__dirname,'../../upload/publishUploadFile/'+id+'/'+name); if(!fs.existsSync(filePath)){ fn(newError("没有附件!"),null); }else{ fn(null,{"name":name,"path":filePath}); } }else{ fn(newError("id或name不能为空"),null); } }catch(e){ fn(newError(e.message),null); } }; /** *接收端上传附件 *@paramreq *@paramfn */ MessageManager.prototype.uploadFile=function(req,fn){ try{ //消息ID varmsgId=req.body.msgId; //消息发送的时间 varmsgSendTime=req.body.msgSendTime.slice(0,10); //消息的标题 vartitle=req.body.title; varreplyId=req.body.replyId; varreplyName=req.body.replyName; varfile=req.file; //文件上传的目录 varuploadFolder=path.join(__dirname,'../../upload/messages/'+msgId+'/'+replyName); //判断文件夹是否存在不存在则创建 toolUtil.mkdirSync(uploadFolder); //组装文件的名称原名称+消息发送时间 varindex=file.originalname.lastIndexOf("."); varfileName=file.originalname.substr(0,index)+'-'+msgSendTime+""; varsuffix=file.originalname.substr(index,file.originalname.length-1); //将上传的文件从临时目录拷贝到指定的目录下 varfileReadStream=fs.createReadStream(file.path); varfileWriteStream=fs.createWriteStream(uploadFolder+"/"+fileName+"."+suffix); fileReadStream.pipe(fileWriteStream); fileWriteStream.on('close',function(){ //删除临时目录下面的文件 toolUtil.emptyDir(file.destination); }); fn(null,{"data":"","message":"上传成功","error_code":200}); }catch(e){ fn(e,{"data":"","message":"上传失败","error_code":e.message}); } }; /** *导出消息的附件文件 *@paramreq *@paramfn */ MessageManager.prototype.exportFile=function(req,fn){ try{ //消息ID varid=req.query.id; //消息名称或标题 varname=req.query.name; if(id&&name){ //名称过长的话,截取前25个字符 if(name.length>25){ name=name.substr(0,24); } //将要压缩得文件夹路径 varmessagePath=path.join(__dirname,'../../upload/messages/'+id); if(!fs.existsSync(messagePath)){ fn(newError("没有附件!"),null); }else{ //生成得临时zip文件目录 varzipPath=path.join(__dirname,'../../upload/temp.zip'); vararchive=archiver('zip',{ //Setsthecompressionlevel. zlib:{level:9} }); //创建临时zip文件 varoutput=fs.createWriteStream(zipPath); archive.pipe(output); //设置需要压缩得文件夹目录以及替换得名称 archive.directory(messagePath,name); archive.finalize(); archive.on('end',function(err){ fn(null,{"name":name,"path":zipPath}); }); archive.on('error',function(err){ fn(newError("压缩文件异常"),null); }); } }else{ fn(newError("id或name不能为空"),null); } }catch(e){ fn(newError(e.message),null); } };
最后是提出的公共方法toolUtil的代码,这个单独做为一个js文件维护。
constpath=require('path'); constfs=require('fs'); /** *创建目录 *@paramdirpath */ exports.mkdirSync=function(dirpath){ if(!fs.existsSync(dirpath)){ varpathtmp; dirpath.split(path.sep).forEach(function(dirname){ if(pathtmp){ pathtmp=path.join(pathtmp,dirname); } else{ pathtmp=dirname; } if(!fs.existsSync(pathtmp)){ fs.mkdirSync(pathtmp); } }); } }; //删除所有的文件(将所有文件夹置空) exports.emptyDir=function(dirpath){ varself=this; //读取该文件夹 varfiles=fs.readdirSync(dirpath); files.forEach(function(file){ varfilePath=dirpath+'/'+file; varstats=fs.statSync(filePath); if(stats.isDirectory()){ self.emptyDir(filePath); }else{ fs.unlinkSync(filePath); } }); };
希望本文所述对大家node.js程序设计有所帮助。