node.js express框架實現(xiàn)文件上傳與下載功能實例詳解
本文實例講述了node.js express框架實現(xiàn)文件上傳與下載功能。分享給大家供大家參考,具體如下:
背景
昨天吉視傳媒的客戶對IPS信息發(fā)布系統(tǒng)又提了一個新需求,就是發(fā)布端發(fā)送消息時需要支持附件的上傳,而接收端可以對發(fā)布端上傳的附件進行下載;接收端回復消息時也需要支持上傳附件,發(fā)布端可以對所有接收端上傳的附件進行打包下載。
功能實現(xiàn)
- 前臺部分
前臺使用webUploader插件即可,這是百度開發(fā)的一款文件上傳組件,具體使用查看它的API即可。這個項目之前開發(fā)的時候前臺使用了angular.js。
$scope.fileName = ""; //創(chuàng)建上傳附件的對象 var $list = $("#thelist"); var uploader = WebUploader.create({ // 選完文件后,是否自動上傳。 auto: false, // swf文件路徑 swf: '../../../lib/webUploader/Uploader.swf', // 文件接收服務端。 server: '/publishUploadFile', // 內部根據(jù)當前運行是創(chuàng)建,可能是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); });
當用戶選擇文件的時候我創(chuàng)建了文件上傳的對象,而在用戶真正發(fā)送消息的時候我添加了相應的參數(shù)并將附件真正的上傳上去,符合我這個項目的業(yè)務邏輯。
if($scope.fileName){ //添加參數(shù) uploader.options.formData.fileId = fileId; uploader.options.formData.fileName = $scope.fileName; uploader.upload(); }
- 后臺部分
路由就不詳細說明了,主要注意的是下載的接口我都是使用的get請求,這樣前臺在請求的時候直接新打開一個窗口拼接了相應的參數(shù)就能下載文件了。下面貼一下action層的代碼:
//發(fā)布端上傳附件 exports.publishUploadFile = function (req, res) { messageMng.publishUploadFile(req, function (err, datas) { res.json(datas); }); }; //下載發(fā)布端上傳的附件 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); }); }; //發(fā)布端導出附件 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層的代碼:
/** * 發(fā)布端上傳附件 * @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); //判斷文件夾是否存在 不存在則創(chuàng)建 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}); } }; /** * 下載發(fā)布端上傳的附件 * @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; //消息發(fā)送的時間 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); //判斷文件夾是否存在 不存在則創(chuàng)建 toolUtil.mkdirSync(uploadFolder); //組裝文件的名稱 原名稱+消息發(fā)送時間 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} }); //創(chuàng)建臨時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'); /** * 創(chuàng)建目錄 * @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程序設計有所幫助。
相關文章
nodejs的http和https下載遠程資源post數(shù)據(jù)實例
這篇文章主要為大家介紹了nodejs的http和https下載遠程資源post數(shù)據(jù)實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-09-09