本文实例讲述了node.js express框架实现文件上传与下载功能。分享给大家供大家参考,具体如下:
背景
昨天吉视传媒的客户对IPS信息发布系统又提了一个新需求,就是发布端发送消息时需要支持附件的上传,而接收端可以对发布端上传的附件进行下载;接收端回复消息时也需要支持上传附件,发布端可以对所有接收端上传的附件进行打包下载。
功能实现
- 前台部分
前台使用webUploader插件即可,这是百度开发的一款文件上传组件,具体使用查看它的API即可。这个项目之前开发的时候前台使用了angular.js。
$scope.fileName = ""; //创建上传附件的对象 var $list = $("#thelist"); var uploader = 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"); //第二种方式 // var path="D:/maven介绍.ppt"; var f = 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层的代码:
/** * 发布端上传附件 * @param req * @param fn */ MessageManager.prototype.publishUploadFile = function (req, fn) { try { //消息ID var fileId = req.body.fileId; var file = req.file; //文件上传的目录 var uploadFolder = path.join(__dirname, '../../upload/publishUploadFile/' + fileId); //判断文件夹是否存在 不存在则创建 toolUtil.mkdirSync(uploadFolder); //将上传的文件从临时目录拷贝到指定的目录下 var fileReadStream = fs.createReadStream(file.path); var fileWriteStream = 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}); } }; /** * 下载发布端上传的附件 * @param req * @param fn */ MessageManager.prototype.exportPublishFile = function (req, fn) { try { //附件ID var id = req.query.id; //附件名称或标题 var name = req.query.name; if (id && name) { //名称过长的话,截取前25个字符 if (name.length > 25) { name = name.substr(0, 24); } //将要压缩得文件夹路径 var filePath = path.join(__dirname, '../../upload/publishUploadFile/' + id + '/' + name); if (!fs.existsSync(filePath)) { fn(new Error("没有附件!"), null); } else { fn(null, {"name": name, "path": filePath}); } } else { fn(new Error("id或name不能为空"), null); } } catch (e) { fn(new Error(e.message), null); } }; /** * 接收端上传附件 * @param req * @param fn */ MessageManager.prototype.uploadFile = function (req, fn) { try { //消息ID var msgId = req.body.msgId; //消息发送的时间 var msgSendTime = req.body.msgSendTime.slice(0, 10); //消息的标题 var title = req.body.title; var replyId = req.body.replyId; var replyName = req.body.replyName; var file = req.file; //文件上传的目录 var uploadFolder = path.join(__dirname, '../../upload/messages/' + msgId + '/' + replyName); //判断文件夹是否存在 不存在则创建 toolUtil.mkdirSync(uploadFolder); //组装文件的名称 原名称+消息发送时间 var index = file.originalname.lastIndexOf("."); var fileName = file.originalname.substr(0, index) + '-' + msgSendTime + ""; var suffix = file.originalname.substr(index, file.originalname.length - 1); //将上传的文件从临时目录拷贝到指定的目录下 var fileReadStream = fs.createReadStream(file.path); var fileWriteStream = 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}); } }; /** * 导出消息的附件文件 * @param req * @param fn */ MessageManager.prototype.exportFile = function (req, fn) { try { //消息ID var id = req.query.id; //消息名称或标题 var name = req.query.name; if (id && name) { //名称过长的话,截取前25个字符 if (name.length > 25) { name = name.substr(0, 24); } //将要压缩得文件夹路径 var messagePath = path.join(__dirname, '../../upload/messages/' + id); if (!fs.existsSync(messagePath)) { fn(new Error("没有附件!"), null); } else { //生成得临时zip文件目录 var zipPath = path.join(__dirname, '../../upload/temp.zip'); var archive = archiver('zip', { // Sets the compression level. zlib: {level: 9} }); //创建临时zip文件 var output = 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(new Error("压缩文件异常"), null); }); } } else { fn(new Error("id或name不能为空"), null); } } catch (e) { fn(new Error(e.message), null); } };
最后是提出的公共方法toolUtil的代码,这个单独做为一个js文件维护。
const path = require('path'); const fs = require('fs'); /** * 创建目录 * @param dirpath */ exports.mkdirSync = function (dirpath){ if (!fs.existsSync(dirpath)) { var pathtmp; 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){ var self = this; //读取该文件夹 var files = fs.readdirSync(dirpath); files.forEach(function(file){ var filePath = dirpath + '/' + file; var stats = fs.statSync(filePath); if(stats.isDirectory()){ self.emptyDir(filePath); }else{ fs.unlinkSync(filePath); } }); };
希望本文所述对大家node.js程序设计有所帮助。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]