使用Express 4.0进行文件上传:req.files未定义。

284

我正在尝试使用Express 4.0实现一个简单的文件上传机制,但在app.post请求体中,req.files一直返回undefined。以下是相关代码:

var bodyParser = require('body-parser');
var methodOverride = require('method-override');
//...
app.use(bodyParser({ uploadDir: path.join(__dirname, 'files'), keepExtensions: true })); 
app.use(methodOverride()); 
//...
app.post('/fileupload', function (req, res) {
  console.log(req.files); 
  res.send('ok'); 
}); 

...以及相应的 Pug 代码:

form(name="uploader", action="/fileupload", method="post", enctype="multipart/form-data")
    input(type="file", name="file", id="file")
    input(type="submit", value="Upload")

解决方案
感谢下面mscdex的回复,我已经改用busboy而不是bodyParser

var fs = require('fs');
var busboy = require('connect-busboy');
//...
app.use(busboy()); 
//...
app.post('/fileupload', function(req, res) {
    var fstream;
    req.pipe(req.busboy);
    req.busboy.on('file', function (fieldname, file, filename) {
        console.log("Uploading: " + filename); 
        fstream = fs.createWriteStream(__dirname + '/files/' + filename);
        file.pipe(fstream);
        fstream.on('close', function () {
            res.redirect('back');
        });
    });
});

2
这在多个文件中如何运作? - chovy
@chovy,使用多个文件应该可以正常工作。 - mscdex
2
我认为可以这样做:app.post('/fileupload',busboy(), function(req, res) { - Shimon Doodkin
好的解决方案。我想指出,在上传之前,您必须在应用程序的主目录中创建一个“./files/”目录,否则您将会收到一个错误。 - sauce
临时文件如何处理?Busboy是否会自动删除它们?我只是没看到任何临时文件在保存到磁盘之前被删除。 - ed-ta
14个回答

234

body-parser 模块只能处理 JSON 和 urlencode 表单提交,无法处理 multipart 数据(如果您要上传文件,这种情况将出现)。

对于 multipart 数据,您需要使用像 connect-busboymulter 或者 connect-multiparty 这样的工具(multiparty/formidable 是最初在 express bodyParser 中使用的工具)。顺便提一下,我正在基于 busboy 开发一个更高级别的层次叫做 reformed。它带有 Express 中间件并且也可以单独使用。


5
谢谢,那很有效。虽然我不得不使用connect-busboy而不是只有busboy。我已经更新了原帖并附上了解决方案。 - safwanc
5
谢谢伙计!我发现connect-multiparty选项是这些选项中最好的! - neciu
“reformed”仍在开发中吗?您在Github上的最后一次提交是在2014年...顺便问一下,您认为处理多部分表单数据的最佳模块是什么?通过“最佳”,我指的是最受支持、最稳定(没有或较少bug)、功能最强大且未来更长久的模块。我选择了“multer”,因为它似乎得到了最好的支持,但我仍然认为它应该得到更多的支持。 - nbro
请问在Express 3.0中使用的multipart是否在4.0中已经失效了?我问这个问题是因为这篇教程使用的是3.4.8版本,可以上传文件而无需任何额外的中间件。http://blog.robertonodi.me/simple-image-upload-with-express/ - thetrystero
@thetrystero,你链接的那个示例的 Github 存储库实际上已经将依赖项检入存储库中。如果你深入挖掘这些依赖项,你会发现 Express 3.x 和 Connect 2.x(其中仍然包含一个多部分模块)已经被包含在内。这就是为什么多部分处理“开箱即用”的原因。 - mscdex
你好 @mscde,请问您能否看一下我遇到的这个问题吗?https://stackoverflow.com/questions/74173460/req-file-and-req-files-are-undefined-when-calling-the-endpoint-from-mobile-apps - SYED ASAD KAZMI

58

这是我通过谷歌搜索得到的:

var fileupload = require("express-fileupload");
app.use(fileupload());

这是一个相当简单的上传机制

app.post("/upload", function(req, res)
{
    var file;

    if(!req.files)
    {
        res.send("File was not found");
        return;
    }

    file = req.files.FormFieldName;  // here is the field name of the form

    res.send("File Uploaded");


});

对于大文件来说速度太慢。 - Eduardo

23

1) 确保文件确实是从客户端发送的。例如,您可以在Chrome控制台中检查它:截图

2) 这是NodeJS后端的基本示例:

const express = require('express');
const fileUpload = require('express-fileupload');
const app = express();

app.use(fileUpload()); // Don't forget this line!

app.post('/upload', function(req, res) {
   console.log(req.files);
   res.send('UPLOADED!!!');
});

12

看起来在 Express 3 中,body-parser 曾经支持上传文件,但在 Express 4 中停止了支持,因为它不再作为 Connect 的依赖项

在查看了mscdex答案中的一些模块后,我发现 express-busboy 是一个更好的替代方案,也是最接近插拔式替换的东西。我注意到唯一的区别在于上传文件的属性。

console.log(req.files) 在使用body-parser (Express 3) 时输出一个对象,看起来像这样:

{ file: 
   { fieldName: 'file',
     originalFilename: '360px-Cute_Monkey_cropped.jpg',
     name: '360px-Cute_Monkey_cropped.jpg'
     path: 'uploads/6323-16v7rc.jpg',
     type: 'image/jpeg',
     headers: 
      { 'content-disposition': 'form-data; name="file"; filename="360px-Cute_Monkey_cropped.jpg"',
        'content-type': 'image/jpeg' },
     ws: 
      WriteStream { /* ... */ },
     size: 48614 } }

使用 express-busboy(Express 4)与 console.log(req.files) 相比:

{ file: 
   { field: 'file',
     filename: '360px-Cute_Monkey_cropped.jpg',
     file: 'uploads/9749a8b6-f9cc-40a9-86f1-337a46e16e44/file/360px-Cute_Monkey_cropped.jpg',
     mimetype: 'image/jpeg',
     encoding: '7bit',
     truncated: false
     uuid: '9749a8b6-f9cc-40a9-86f1-337a46e16e44' } }

9

Multer是一种中间件,可处理“multipart/form-data”,并将上传的文件和表单数据以request.files和request.body的形式通过请求使其对我们可用。

安装Multer:- npm install multer --save

在.html文件中:

<form method="post" enctype="multipart/form-data" action="/upload">
    <input type="hidden" name="msgtype" value="2"/>
    <input type="file" name="avatar" />
    <input type="submit" value="Upload" />
</form>

在 .js 文件中:

var express = require('express');
var multer = require('multer');
var app = express();
var server = require('http').createServer(app);
var port = process.env.PORT || 3000;
var upload = multer({ dest: 'uploads/' });

app.use(function (req, res, next) {
  console.log(req.files); // JSON Object
  next();
});

server.listen(port, function () {
  console.log('Server successfully running at:-', port);
});

app.get('/', function(req, res) {
  res.sendFile(__dirname + '/public/file-upload.html');
})

app.post('/upload', upload.single('avatar'),  function(req, res) {
  console.log(req.files); // JSON Object
});

希望这能帮到你!

4
请使用以下代码。
app.use(fileUpload());

7
var fileupload = require("express-fileupload"); app.use(fileupload()); - Saurabh Agarwal

3

除了以上的答案,您还可以进一步优化使用 express-fileupload 的方式,只在需要它的单个路由上添加,而不是每个路由都添加。

let fileupload = require("express-fileupload");

...

//Make sure to call fileUpload to get the true handler
app.post("/upload", fileupload(), function(req, res){

...

});

2
我花了十几次超时才意识到你忘记正确调用中间件了。 - veteri
1
@veteri,真的… - wokoro douye samuel

2

安装某些功能需要一个包,有很多选择,但我个人更喜欢"express-fileupload"。只需在终端中输入"npm i express-fileupload"命令进行安装,然后在根文件中使用它即可。

const fileUpload = require("express-fileupload");
app.use(fileUpload());

1

我在methodOverride中间件之前将multer添加为全局中间件,这样在router.put中也可以使用。

const upload = multer({
    storage: storage
}).single('featuredImage');
app.use(upload);

app.use(methodOverride(function (req, res) {
  ...
}));


0

问题解决了!!!!!

原来storage函数根本没有运行过。 因为我必须包含app.use(upload),并将upload = multer({storage}).single('file');作为参数。

 let storage = multer.diskStorage({
        destination: function (req, file, cb) {
            cb(null, './storage')
          },
          filename: function (req, file, cb) {
            console.log(file) // this didn't print anything out so i assumed it was never excuted
            cb(null, file.fieldname + '-' + Date.now())
          }
    });

    const upload = multer({storage}).single('file');

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接