node.js express框架实现文件上传与下载功能实例详解


Posted in Javascript onOctober 15, 2019

本文实例讲述了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程序设计有所帮助。

Javascript 相关文章推荐
javascript 弹出窗口中是否显示地址栏的实现代码
Apr 14 Javascript
基于mootools 1.3框架下的图片滑动效果代码
Apr 22 Javascript
jquery 回车事件实现代码
Aug 23 Javascript
javascipt:filter过滤介绍及使用
Sep 10 Javascript
JS小游戏之象棋暗棋源码详解
Sep 25 Javascript
js分页工具实例
Jan 28 Javascript
JavaScript获得当前网页来源页面(即上一页)的方法
Apr 03 Javascript
数据结构中的各种排序方法小结(JS实现)
Jul 23 Javascript
JS原型链 详解及示例代码
Sep 06 Javascript
js仿拉勾网首页穿墙广告效果
Mar 08 Javascript
Validform验证时可以为空否则按照指定格式验证
Oct 20 Javascript
详解Vue底部导航栏组件
May 02 Javascript
ES6 Promise对象概念及用法实例详解
Oct 15 #Javascript
详解Vue.js 作用域、slot用法(单个slot、具名slot)
Oct 15 #Javascript
vue vantUI实现文件(图片、文档、视频、音频)上传(多文件)
Oct 15 #Javascript
ES6中let、const的区别及变量的解构赋值操作方法实例分析
Oct 15 #Javascript
vue使用@scroll监听滚动事件时,@scroll无效问题的解决方法详解
Oct 15 #Javascript
vue实现树形结构样式和功能的实例代码
Oct 15 #Javascript
谈谈IntersectionObserver懒加载的具体使用
Oct 15 #Javascript
You might like
php 编写安全的代码时容易犯的错误小结
2010/05/20 PHP
php使用session二维数组实例
2014/11/06 PHP
Yii框架在页面输出执行sql语句以方便调试的实现方法
2014/12/24 PHP
必须收藏的23个php实用代码片段
2016/02/02 PHP
Android App中DrawerLayout抽屉效果的菜单编写实例
2016/03/21 PHP
CI框架支持$_GET的两种实现方法
2016/05/18 PHP
PHP实现从PostgreSQL数据库检索数据分页显示及根据条件查找数据示例
2018/06/09 PHP
PHP大文件分块上传功能实例详解
2019/07/22 PHP
JavaScript 获取用户客户端操作系统版本
2009/08/25 Javascript
ExtJS4 组件化编程,动态加载,面向对象,Direct
2011/05/12 Javascript
jQuery中的pushStack实现原理和应用实例
2015/02/03 Javascript
JavaScript让Textarea支持tab按键的方法
2015/06/26 Javascript
一道关于JavaScript变量作用域的面试题
2016/03/08 Javascript
深入理解JavaScript 函数
2016/06/06 Javascript
微信小程序 Audio API详解及实例代码
2016/09/30 Javascript
easyUI实现类似搜索框关键词自动提示功能示例代码
2016/12/27 Javascript
jquery实现下拉框多选方法介绍
2017/01/03 Javascript
angularjs指令之绑定策略(@、=、&)
2017/04/13 Javascript
基于iScroll实现内容滚动效果
2018/03/21 Javascript
原生JS+HTML5实现的可调节写字板功能示例
2018/08/30 Javascript
微信小程序登录session的使用
2019/03/17 Javascript
[02:46]2014DOTA2国际邀请赛 选手为你解读比赛MVP充满梦想
2014/07/09 DOTA
[02:30]联想杯DOTA2完美世界全国高校联赛—北京站现场
2015/11/16 DOTA
python使用PyV8执行javascript代码示例分享
2013/12/04 Python
python关闭windows进程的方法
2015/04/18 Python
python中子类调用父类函数的方法示例
2017/08/18 Python
Python中property函数用法实例分析
2018/06/04 Python
使用浏览器访问python写的服务器程序
2019/10/10 Python
Python unittest单元测试框架实现参数化
2020/04/29 Python
什么是Python中的顺序表
2020/06/02 Python
python 实现图片裁剪小工具
2021/02/02 Python
玩转CSS3色彩
2010/01/16 HTML / CSS
印刷工程专业应届生求职信
2013/09/29 职场文书
2015年小学语文工作总结
2015/05/25 职场文书
公司保洁员管理制度
2015/08/04 职场文书
TensorFlow的自动求导原理分析
2021/05/26 Python