精华 NodeJS教程--基于ExpressJS框架的文件上传
发布于 3年前 作者 letonlife 17908 次浏览

本文是翻译的一篇文章,原文地址:Handle File Uploads in Express (Node.js).

在NodeJS发展早期上传文件是一个较难操作的功能,随后出现了formidable。它开了一个好头,然而上传文件仍然不是那么容易。在此之后又出现了基于原文作者的教程(tutorial on handling POST requests in Express)而实现的工具connect-form。它让文件上传的过程显得简单了一些。

随着NodeJS社区的飞速发展,让上传文件这个功能变得简单得以实现肯定不会需要太长时间。现在这个功能已经得以实现—在ExpressJS中你可以很容易的上传文件。

在Express中上传文件时不再需要依赖其他的模块,文件上传可以很好工作于Express框架内。就像使用req.body一样,现在你可以使用req.files来获得你上传的文件中的具体信息。

如果你不需要指定上传文件的目录,那么你不需要对app.js做任何的改变,文件将默认上传到’/tmp’目录。但是如果你希望对你上传的文件指定特定目录,你仅仅需要修改一下内容:

app.use(express.bodyParser());

修改为 :

app.use(express.bodyParser({uploadDir:'./uploads'}));

这一配置项可以再ExpressJS的V2.5.4之后的版本中发现,具体可以参看ExpressJS的源代码,具体在express.bodyParser的第16行。在上面的示例中。文件将被上传到与express应用同级目录下的uploads目录中。

为了更好说明在POST过程中文件上传时一些细节,我们建立如下的一个表单提交页面:

<form method="post" enctype="multipart/form-data" action="/file-upload">
  <input type="text" name="username">
  <input type="password" name="password">
  <input type="file" name="thumbnail">
  <input type="submit">
</form>

服务端在处理上面的表单提交时的方法如下:

app.post('/file-upload', function(req, res, next) {
  console.log(req.body);
  console.log(req.files);
});

看看就是这么简单!

在上面的示例中,我们可以通过调用req.files.thumbnail对象来获得我们上传的文件的具体信息,对象的名称取决你上在form表单的定义。如上所示,我们使用的名称是thumbnail,因此我们可以通过req.files.thumbnail来获得它的信息。

一个文件包含以下常用的属性信息:

  • size ---- 文件大小(bytes)
  • path ---- 文件上传后的路径
  • name ---- 文件的原始文件名称.
  • type ----文件类型

针对上面的示例来说, 文件大小:req.files.thumbnail.size 文件类型: req.files.thumbnail.type 原文件名: req.files.thumbnail.name

需要注意的是,默认的上传目录或你指定的上传目录都只是临时目录,因此你还需要将上传的文件移动相应的文件目录。下面的示例代码演示了如何将上传的文件移动到’/images’目录下:

   // 移动文件需要使用fs模块
   var fs = require('fs');
   app.post('/file-upload', function(req, res) {
     // 获得文件的临时路径
     var tmp_path = req.files.thumbnail.path;
    // 指定文件上传后的目录 - 示例为"images"目录。 
    var target_path = './public/images/' + req.files.thumbnail.name;
    // 移动文件
    fs.rename(tmp_path, target_path, function(err) {
      if (err) throw err;
      // 删除临时文件夹文件, 
      fs.unlink(tmp_path, function() {
         if (err) throw err;
         res.send('File uploaded to: ' + target_path + ' - ' + req.files.thumbnail.size + ' bytes');
      });
    });
  };

你可以将上传目录指定为任何你可以在当前操作系统环境下可以访问的目录。如果你希望这些文件可以被公开访问,你可以将文件拷贝到应用的静态目录’/public’下。需要注意的是,如果需要将文件拷贝到public或其子目录下,请确保文件夹存在,且可以被写入。否则会出现’Error: ENOENT, no such file’等异常信息。

现在在ExpressJS中上传文件将是一件非常简单的事情。如果你的原有应用需要使用文件上传,那么我建议你将应用升级到最新的ExpressJS框架,并且修改你的程序以保证功能可以正常工作。

32 回复
app.post('/file-upload', function(req, res) {
 // 获得文件的临时路径
 var tmp_path = req.files.thumbnail.path;
// 指定文件上传后的目录 - 示例为"images"目录。 
var target_path = './public/images/' + req.files.thumbnail.name;
// 移动文件
fs.rename(tmp_path, target_path, function(err) {
  if (err) throw err;
  // 删除临时文件夹文件, 
  fs.unlink(tmp_path, function() {
     if (err) throw err;
     res.send('File uploaded to: ' + target_path + ' - ' + req.files.thumbnail.size + ' bytes');
  });
});

这个方法中,在执行哪一步时文件已经上传完毕?是rename前就上传完毕了嘛?如果不是的话(不同步)不就错了?

在rename前 ,文件已经通过express上传到临时目录下了。不过我还是建议再做一个完整性检查,比较下临时文件夹下的文件和对象属性大小是否相同。具体你可以在本地测试下。

rename 就上传完毕了?

是的。因为在express会先处理上传的文件,并且将对象数据产生好放入req.files中。然后再路由请求到file-upload方法。要验证是不是这样,可以做个小实验嘛。光看代码并不能解答疑惑。并且这个上传是基于Express的,那么肯定express是作处理的。

@letonlife 直接问题,哈哈,不好意思,马上试一下,呵呵

@letonlife 刚刚测试过了,确实能用,哈哈,没想到这个rename功能竟然能带路径的去rename

不过,当没有传递文件时也会生成一个缓存文件,这时rename好像会报错,

@letonlife 刚刚测试过了,确实能用,哈哈,没想到这个rename功能竟然能带路径的去rename

不过,当没有传递文件时也会生成一个缓存文件,这时rename好像会报错,

小弟刚刚开始接触node 有点弱弱的问题想问问 首先是 html文件如何转换成 jade格式 比如这句

<form method="post" enctype="multipart/form-data" action="/file-upload"> 应该如何写成jade格式呢?

app.post('/file-upload’, function(req, res) {…}

还有以上这段代码 是直接加到app.js中就可以吗?

<form method="post" enctype="multipart/form-data" action="/file-upload">

如何转成jade文件

@jankuo 在沒有傳遞文件的時候 可以js前端判斷一下吧- - express的上傳估計是封裝的

form(method="post", enctype="multipart/form-data", action="/upload")
  fieldset
    legend General

    p
      label(for="user[pic]") Picture:
        input(type="file", name="pics")
  p.buttons
    input(type="submit", value="Save")

在windows下,如果你的node安装在非系统盘,按照上文给出的的方法,应该会出现诸如 Error: EXDEV, rename … 之类的错误。

这是因为express默认上传临时目录是在系统盘,而windows下rename不可以跨区操作。查看了下nodeclud的代码,发现它是用下面的方式修改express默认上传临时目录:

var ndir = require('ndir');
var mod = require('express/node_modules/connect/node_modules/formidable');
var upload_path = path.join(path.dirname(__dirname), 'public/user_data/images');
ndir.mkdir(upload_path, function (err) {
  if (err) {
    throw err;
  }
  mod.IncomingForm.UPLOAD_DIR = upload_path;
});

thank you 没有及时回复 不好意思了 @jinwyp

ndir这个模块怎么没有看到过~

如果是上传多个文件呢?该怎么处理?

请问你这个问题有答案了吗,我也想知道这个问题呀,另外你能把这个功能的完整代码贴出来不,我也是刚接触node

用webstorm运行了下。。。有报错 Error: EXDEV, rename ‘C:\DOCUME~1\WB-YAN~1\LOCALS~1\Temp\370f31ad9ec2046b7e986eea8e50cb9c’ 这个是怎么回事的呀

用webstorm运行了下。。。有报错 Error: EXDEV, rename ‘C:\DOCUME~1\WB-YAN~1\LOCALS~1\Temp\370f31ad9ec2046b7e986eea8e50cb9c’ ------我的node安装在系统盘上的,初了这个还有其他的原因吗?

不错,使用Express还是很简单的.

按照顶楼提示改了文件上传目录为files,但有时候会出现下面的错误 Error:EPERM,rename 'D:\chat\chat\files\e4fbacfeb6dd020732c592e7621d6。有时候挺正确。不知道哪里出问题了。请指教

安装html2jade 然后命令行 htmljade xxx.html

这样写似乎会好些: <form method="post", enctype="multipart/form-data", action="/file-upload">

有个问题,怎么阻止上传,总不能让用户无限制上传吧,服务器要爆的。

<form method="post" enctype="multipart/form-data" action="/file-upload"> <input type="text" name="username"> <input type="password" name="password"> <input type="file" name="thumbnail"> <input type="submit"> </form>

问一下,怎么给文件随机命名,防止名字重复?
有类似filetime之类的东西吗?

字段里有 username,password 要怎么获取? req.body.username, req.files.usernaem 都是空

Error:EPERM,rename这个问题解决了么?可否告知一下?

为何我执行出来: console.log(req.body); console.log(req.files); 结果分别为: {} undefined 呢?

难道是因为我的express没有安装在系统盘的缘故? 我用的windows,express没安装在c盘。

@sihao1234 项目文件移到系统盘后情况依旧。 express的版本为:3.4.8 换了express 3.5.2后情况依旧。 这个奇怪了。。。。求解!

如果去掉enctype="multipart/form-data" console.log(req.body); console.log(req.files); 结果分别为: {username:"abc",password:"123" ,thumbnail:"a.txt">} undefined 哪里的问题嘞?

原来是这样的:

app.use(express.bodyParser({uploadDir:’./tmp’})); //一定要放在app.use(app.router);前面 app.use(app.router);

找了好久才找到原因。。。

body-parser1.8.4不支持multipart了

回到顶部