node.js express框架實(shí)現(xiàn)文件上傳與下載功能實(shí)例詳解
本文實(shí)例講述了node.js express框架實(shí)現(xiàn)文件上傳與下載功能。分享給大家供大家參考,具體如下:
背景
昨天吉視傳媒的客戶(hù)對(duì)IPS信息發(fā)布系統(tǒng)又提了一個(gè)新需求,就是發(fā)布端發(fā)送消息時(shí)需要支持附件的上傳,而接收端可以對(duì)發(fā)布端上傳的附件進(jìn)行下載;接收端回復(fù)消息時(shí)也需要支持上傳附件,發(fā)布端可以對(duì)所有接收端上傳的附件進(jìn)行打包下載。
功能實(shí)現(xiàn)
- 前臺(tái)部分
前臺(tái)使用webUploader插件即可,這是百度開(kāi)發(fā)的一款文件上傳組件,具體使用查看它的API即可。這個(gè)項(xiàng)目之前開(kāi)發(fā)的時(shí)候前臺(tái)使用了angular.js。
$scope.fileName = "";
//創(chuàng)建上傳附件的對(duì)象
var $list = $("#thelist");
var uploader = WebUploader.create({
// 選完文件后,是否自動(dòng)上傳。
auto: false,
// swf文件路徑
swf: '../../../lib/webUploader/Uploader.swf',
// 文件接收服務(wù)端。
server: '/publishUploadFile',
// 內(nèi)部根據(jù)當(dāng)前運(yùn)行是創(chuàng)建,可能是input元素,也可能是flash.
pick : {
id : '#filePicker',
//只能選擇一個(gè)文件上傳
multiple: false
},
// pick :'#filePicker',
method: 'POST',
});
uploader.on('fileQueued', function (file) {
$scope.fileName = file.name;
$list.html("");
$list.html(file.name);
});
當(dāng)用戶(hù)選擇文件的時(shí)候我創(chuàng)建了文件上傳的對(duì)象,而在用戶(hù)真正發(fā)送消息的時(shí)候我添加了相應(yīng)的參數(shù)并將附件真正的上傳上去,符合我這個(gè)項(xiàng)目的業(yè)務(wù)邏輯。
if($scope.fileName){
//添加參數(shù)
uploader.options.formData.fileId = fileId;
uploader.options.formData.fileName = $scope.fileName;
uploader.upload();
}
- 后臺(tái)部分
路由就不詳細(xì)說(shuō)明了,主要注意的是下載的接口我都是使用的get請(qǐng)求,這樣前臺(tái)在請(qǐng)求的時(shí)候直接新打開(kāi)一個(gè)窗口拼接了相應(yīng)的參數(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ā)布端導(dǎo)出附件
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解壓報(bào)錯(cuò)
// 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);
}
});
};
這里著重說(shuō)一下下載zip時(shí)使用download下載完的壓縮包解壓會(huì)報(bào)錯(cuò),使用第二種方法完美解決。
然后是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);
//將上傳的文件從臨時(shí)目錄拷貝到指定的目錄下
var fileReadStream = fs.createReadStream(file.path);
var fileWriteStream = fs.createWriteStream(uploadFolder + "/" + file.originalname);
fileReadStream.pipe(fileWriteStream);
fileWriteStream.on('close', function () {
// 刪除臨時(shí)目錄下面的文件
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;
//附件名稱(chēng)或標(biāo)題
var name = req.query.name;
if (id && name) {
//名稱(chēng)過(guò)長(zhǎng)的話,截取前25個(gè)字符
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("沒(méi)有附件!"), 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ā)送的時(shí)間
var msgSendTime = req.body.msgSendTime.slice(0, 10);
//消息的標(biāo)題
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);
//組裝文件的名稱(chēng) 原名稱(chēng)+消息發(fā)送時(shí)間
var index = file.originalname.lastIndexOf(".");
var fileName = file.originalname.substr(0, index) + '-' + msgSendTime + "";
var suffix = file.originalname.substr(index, file.originalname.length - 1);
//將上傳的文件從臨時(shí)目錄拷貝到指定的目錄下
var fileReadStream = fs.createReadStream(file.path);
var fileWriteStream = fs.createWriteStream(uploadFolder + "/" + fileName + "." + suffix);
fileReadStream.pipe(fileWriteStream);
fileWriteStream.on('close', function () {
//刪除臨時(shí)目錄下面的文件
toolUtil.emptyDir(file.destination);
});
fn(null, {"data": "", "message": "上傳成功", "error_code": 200});
} catch (e) {
fn(e, {"data": "", "message": "上傳失敗", "error_code": e.message});
}
};
/**
* 導(dǎo)出消息的附件文件
* @param req
* @param fn
*/
MessageManager.prototype.exportFile = function (req, fn) {
try {
//消息ID
var id = req.query.id;
//消息名稱(chēng)或標(biāo)題
var name = req.query.name;
if (id && name) {
//名稱(chēng)過(guò)長(zhǎng)的話,截取前25個(gè)字符
if (name.length > 25) {
name = name.substr(0, 24);
}
//將要壓縮得文件夾路徑
var messagePath = path.join(__dirname, '../../upload/messages/' + id);
if (!fs.existsSync(messagePath)) {
fn(new Error("沒(méi)有附件!"), null);
} else {
//生成得臨時(shí)zip文件目錄
var zipPath = path.join(__dirname, '../../upload/temp.zip');
var archive = archiver('zip', {
// Sets the compression level.
zlib: {level: 9}
});
//創(chuàng)建臨時(shí)zip文件
var output = fs.createWriteStream(zipPath);
archive.pipe(output);
//設(shè)置需要壓縮得文件夾目錄 以及替換得名稱(chēng)
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的代碼,這個(gè)單獨(dú)做為一個(gè)js文件維護(hù)。
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);
}
});
};
希望本文所述對(duì)大家node.js程序設(shè)計(jì)有所幫助。
相關(guān)文章
搭建一個(gè)Koa后端項(xiàng)目腳手架的方法步驟
這篇文章主要介紹了搭建一個(gè)Koa后端項(xiàng)目腳手架的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05
nodejs的http和https下載遠(yuǎn)程資源post數(shù)據(jù)實(shí)例
這篇文章主要為大家介紹了nodejs的http和https下載遠(yuǎn)程資源post數(shù)據(jù)實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
node實(shí)現(xiàn)mock-plugin中間件的方法
這篇文章主要介紹了node實(shí)現(xiàn)mock-plugin中間件的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
Node 創(chuàng)建第一個(gè)服務(wù)器應(yīng)用的操作方法
Node.js是一個(gè)基于Chrome V8引擎的JavaScript運(yùn)行環(huán)境,可以用于構(gòu)建高性能的網(wǎng)絡(luò)應(yīng)用程序,它采用事件驅(qū)動(dòng)、非阻塞I/O模型,使得程序可以以高效地方式處理并發(fā)請(qǐng)求,這篇文章主要介紹了Node 創(chuàng)建第一個(gè)服務(wù)器應(yīng)用,需要的朋友可以參考下2024-02-02
node.js中的fs.futimes方法使用說(shuō)明
這篇文章主要介紹了node.js中的fs.futimes方法使用說(shuō)明,本文介紹了fs.futimes方法說(shuō)明、語(yǔ)法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12
node.js中module模塊的功能理解與用法實(shí)例分析
這篇文章主要介紹了node.js中module模塊的功能理解與用法,結(jié)合實(shí)例形式分析了node.js module模塊的基本功能、原理、用法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2020-02-02

