经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » Node.js » 查看文章
node.js express框架实现文件上传与下载功能实例详解
来源:jb51  时间:2019/10/15 13:24:16  对本文有异议

本文实例讲述了node.js express框架实现文件上传与下载功能。分享给大家供大家参考,具体如下:

背景

昨天吉视传媒的客户对IPS信息发布系统又提了一个新需求,就是发布端发送消息时需要支持附件的上传,而接收端可以对发布端上传的附件进行下载;接收端回复消息时也需要支持上传附件,发布端可以对所有接收端上传的附件进行打包下载。

功能实现

  • 前台部分

前台使用webUploader插件即可,这是百度开发的一款文件上传组件,具体使用查看它的API即可。这个项目之前开发的时候前台使用了angular.js。

  1. $scope.fileName = "";
  2. //创建上传附件的对象
  3. var $list = $("#thelist");
  4. var uploader = WebUploader.create({
  5. // 选完文件后,是否自动上传。
  6. auto: false,
  7. // swf文件路径
  8. swf: '../../../lib/webUploader/Uploader.swf',
  9. // 文件接收服务端。
  10. server: '/publishUploadFile',
  11. // 内部根据当前运行是创建,可能是input元素,也可能是flash.
  12. pick : {
  13. id : '#filePicker',
  14. //只能选择一个文件上传
  15. multiple: false
  16. },
  17. // pick :'#filePicker',
  18. method: 'POST',
  19. });
  20. uploader.on('fileQueued', function (file) {
  21. $scope.fileName = file.name;
  22. $list.html("");
  23. $list.html(file.name);
  24. });
  25.  

当用户选择文件的时候我创建了文件上传的对象,而在用户真正发送消息的时候我添加了相应的参数并将附件真正的上传上去,符合我这个项目的业务逻辑。

  1. if($scope.fileName){
  2. //添加参数
  3. uploader.options.formData.fileId = fileId;
  4. uploader.options.formData.fileName = $scope.fileName;
  5. uploader.upload();
  6. }
  7.  
  • 后台部分

路由就不详细说明了,主要注意的是下载的接口我都是使用的get请求,这样前台在请求的时候直接新打开一个窗口拼接了相应的参数就能下载文件了。下面贴一下action层的代码:

  1. //发布端上传附件
  2. exports.publishUploadFile = function (req, res) {
  3. messageMng.publishUploadFile(req, function (err, datas) {
  4. res.json(datas);
  5. });
  6. };
  7. //下载发布端上传的附件
  8. exports.exportPublishFile = function (req, res) {
  9. messageMng.exportPublishFile(req, function (err, datas) {
  10. if (err) {
  11. res.set({
  12. "Content-Disposition": "attachment;filename=" + encodeURI("error.txt")
  13. });
  14. res.write(err.message);
  15. res.end();
  16. } else {
  17. res.download(datas.path, encodeURI(datas.name));
  18. }
  19. });
  20. };
  21. //接收端上传附件
  22. exports.uploadFile = function (req, res) {
  23. messageMng.uploadFile(req, function (err, datas) {
  24. res.json(datas);
  25. });
  26. };
  27. //发布端导出附件
  28. exports.exportFile = function (req, res) {
  29. messageMng.exportFile(req, function (err, datas) {
  30. if (err) {
  31. res.set({
  32. "Content-Disposition": "attachment;filename=" + encodeURI("error.txt")
  33. });
  34. res.write(err.message);
  35. res.end();
  36. } else {
  37. //第一种方式 下载完的zip解压报错
  38. // res.download(datas.path, datas.name + ".zip");
  39. //第二种方式
  40. // var path="D:/maven介绍.ppt";
  41. var f = fs.createReadStream(datas.path);
  42. res.writeHead(200, {
  43. 'Content-Type': 'application/force-download',
  44. 'Content-Disposition': 'attachment; filename='+ encodeURI(datas.name) + '.zip'
  45. });
  46. f.pipe(res);
  47. }
  48. });
  49. };
  50.  

这里着重说一下下载zip时使用download下载完的压缩包解压会报错,使用第二种方法完美解决。

然后是service层的代码:

  1. /**
  2. * 发布端上传附件
  3. * @param req
  4. * @param fn
  5. */
  6. MessageManager.prototype.publishUploadFile = function (req, fn) {
  7. try {
  8. //消息ID
  9. var fileId = req.body.fileId;
  10. var file = req.file;
  11. //文件上传的目录
  12. var uploadFolder = path.join(__dirname, '../../upload/publishUploadFile/' + fileId);
  13. //判断文件夹是否存在 不存在则创建
  14. toolUtil.mkdirSync(uploadFolder);
  15. //将上传的文件从临时目录拷贝到指定的目录下
  16. var fileReadStream = fs.createReadStream(file.path);
  17. var fileWriteStream = fs.createWriteStream(uploadFolder + "/" + file.originalname);
  18. fileReadStream.pipe(fileWriteStream);
  19. fileWriteStream.on('close', function () {
  20. // 删除临时目录下面的文件
  21. toolUtil.emptyDir(file.destination);
  22. });
  23. fn(null, {"data": "", "message": "上传成功", "error_code": 200});
  24. } catch (e) {
  25. fn(e, {"data": "", "message": "上传失败", "error_code": e.message});
  26. }
  27. };
  28. /**
  29. * 下载发布端上传的附件
  30. * @param req
  31. * @param fn
  32. */
  33. MessageManager.prototype.exportPublishFile = function (req, fn) {
  34. try {
  35. //附件ID
  36. var id = req.query.id;
  37. //附件名称或标题
  38. var name = req.query.name;
  39. if (id && name) {
  40. //名称过长的话,截取前25个字符
  41. if (name.length > 25) {
  42. name = name.substr(0, 24);
  43. }
  44. //将要压缩得文件夹路径
  45. var filePath = path.join(__dirname, '../../upload/publishUploadFile/' + id + '/' + name);
  46. if (!fs.existsSync(filePath)) {
  47. fn(new Error("没有附件!"), null);
  48. } else {
  49. fn(null, {"name": name, "path": filePath});
  50. }
  51. } else {
  52. fn(new Error("id或name不能为空"), null);
  53. }
  54. } catch (e) {
  55. fn(new Error(e.message), null);
  56. }
  57. };
  58. /**
  59. * 接收端上传附件
  60. * @param req
  61. * @param fn
  62. */
  63. MessageManager.prototype.uploadFile = function (req, fn) {
  64. try {
  65. //消息ID
  66. var msgId = req.body.msgId;
  67. //消息发送的时间
  68. var msgSendTime = req.body.msgSendTime.slice(0, 10);
  69. //消息的标题
  70. var title = req.body.title;
  71. var replyId = req.body.replyId;
  72. var replyName = req.body.replyName;
  73. var file = req.file;
  74. //文件上传的目录
  75. var uploadFolder = path.join(__dirname, '../../upload/messages/' + msgId + '/' + replyName);
  76. //判断文件夹是否存在 不存在则创建
  77. toolUtil.mkdirSync(uploadFolder);
  78. //组装文件的名称 原名称+消息发送时间
  79. var index = file.originalname.lastIndexOf(".");
  80. var fileName = file.originalname.substr(0, index) + '-' + msgSendTime + "";
  81. var suffix = file.originalname.substr(index, file.originalname.length - 1);
  82. //将上传的文件从临时目录拷贝到指定的目录下
  83. var fileReadStream = fs.createReadStream(file.path);
  84. var fileWriteStream = fs.createWriteStream(uploadFolder + "/" + fileName + "." + suffix);
  85. fileReadStream.pipe(fileWriteStream);
  86. fileWriteStream.on('close', function () {
  87. //删除临时目录下面的文件
  88. toolUtil.emptyDir(file.destination);
  89. });
  90. fn(null, {"data": "", "message": "上传成功", "error_code": 200});
  91. } catch (e) {
  92. fn(e, {"data": "", "message": "上传失败", "error_code": e.message});
  93. }
  94. };
  95. /**
  96. * 导出消息的附件文件
  97. * @param req
  98. * @param fn
  99. */
  100. MessageManager.prototype.exportFile = function (req, fn) {
  101. try {
  102. //消息ID
  103. var id = req.query.id;
  104. //消息名称或标题
  105. var name = req.query.name;
  106. if (id && name) {
  107. //名称过长的话,截取前25个字符
  108. if (name.length > 25) {
  109. name = name.substr(0, 24);
  110. }
  111. //将要压缩得文件夹路径
  112. var messagePath = path.join(__dirname, '../../upload/messages/' + id);
  113. if (!fs.existsSync(messagePath)) {
  114. fn(new Error("没有附件!"), null);
  115. } else {
  116. //生成得临时zip文件目录
  117. var zipPath = path.join(__dirname, '../../upload/temp.zip');
  118. var archive = archiver('zip', {
  119. // Sets the compression level.
  120. zlib: {level: 9}
  121. });
  122. //创建临时zip文件
  123. var output = fs.createWriteStream(zipPath);
  124. archive.pipe(output);
  125. //设置需要压缩得文件夹目录 以及替换得名称
  126. archive.directory(messagePath, name);
  127. archive.finalize();
  128. archive.on('end', function (err) {
  129. fn(null, {"name": name, "path": zipPath});
  130. });
  131. archive.on('error', function (err) {
  132. fn(new Error("压缩文件异常"), null);
  133. });
  134. }
  135. } else {
  136. fn(new Error("id或name不能为空"), null);
  137. }
  138. } catch (e) {
  139. fn(new Error(e.message), null);
  140. }
  141. };
  142.  

最后是提出的公共方法toolUtil的代码,这个单独做为一个js文件维护。

  1. const path = require('path');
  2. const fs = require('fs');
  3. /**
  4. * 创建目录
  5. * @param dirpath
  6. */
  7. exports.mkdirSync = function (dirpath){
  8. if (!fs.existsSync(dirpath)) {
  9. var pathtmp;
  10. dirpath.split(path.sep).forEach(function(dirname) {
  11. if (pathtmp) {
  12. pathtmp = path.join(pathtmp, dirname);
  13. }
  14. else {
  15. pathtmp = dirname;
  16. }
  17. if (!fs.existsSync(pathtmp)) {
  18. fs.mkdirSync(pathtmp);
  19. }
  20. });
  21. }
  22. };
  23. //删除所有的文件(将所有文件夹置空)
  24. exports.emptyDir = function(dirpath){
  25. var self = this;
  26. //读取该文件夹
  27. var files = fs.readdirSync(dirpath);
  28. files.forEach(function(file){
  29. var filePath = dirpath + '/' + file;
  30. var stats = fs.statSync(filePath);
  31. if(stats.isDirectory()){
  32. self.emptyDir(filePath);
  33. }else{
  34. fs.unlinkSync(filePath);
  35. }
  36. });
  37. };
  38.  

希望本文所述对大家node.js程序设计有所帮助。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号