记一次 Node.js 的 formidable 模块上传文件时 files 为空的问题, 顺便整理下相关内容

前言

使用原生来写 POST 的处理, 比较复杂, 而且文件上传业务比较的难写, 所以肯定有人造好了相关的轮子, 也就是 Node.js 的第三方模块 formidable , 使用 formidable 当做中间件用来处理文件上传非常方便, 而且可以配合其他模块对文件进行相应的命名。
不过在对 formidable 学习过程中出现了一个问题, 就是上传文件时, form.parse() 回调函数中 files 对象是个空对象。 就排查了挺久, 然后就以为问题是出在服务器端的代码上, 百度上关于这个问题也是寥寥无几, 众说纷纭。 没想到最后是前台页面 form 表单的问题, 也就是在 <\form> 标签中我少写了一个 enctype 属性, 在使用包含文件上传控件的表单时,必须使用 enctype=”multipart/form-data” 也就是不对字符进行编码。


安装 formidable 模块

首先这里是 formidable 的 Github 地址, 可以 clone 下来, 也可以使用 npm 来安装

1
npm i -S formidable

该模块放在 node_modules 文件夹中


前台提交表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<form action="http://localhost:8080/postmsg" enctype="multipart/form-data" method="post">
<p>姓名: <input type="text" name="name"></p>
<p>年龄: <input type="text" name="age"></p>
<p>
性别:
<input type="radio" name="sex" value="男">
<input type="radio" name="sex" value="女">
</p>
<p>
能力:
<input type="checkbox" name="ability" value="Python">Python
<input type="checkbox" name="ability" value="C">C
<input type="checkbox" name="ability" value="Java">Java
<input type="checkbox" name="ability" value="JavaScript">JavaScript
</p>
<p>
照片: <input type="file" name="photo">
</p>
<button type="submit" name="button">提交</button>
</form>

包含文件上传的表单一定要写 enctype 属性, 并且值要是 multipart/form-data。


后台服务器处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 引入相关模块
const http = require("http"),
url = require("url"),
util = require("util"),
formidable = require("formidable");
uuidv1 = require("uuid/v1");
path = require("path");
fs = require("fs");

// 创建服务器
http.createServer((req, res) => {
if (req.url === "/postmsg" && req.method.toLowerCase() === "post") {
// 实例化一个传入表单
let form = formidable.IncomingForm();
// 设置文件存储目录
form.uploadDir = "./uploadDir";
// 解析传入数据
form.parse(req, (err, fields, files) => {
if (!err) {
// 获取随机的名字
let name = uuidv1();
// 获取文件后缀名
let extName = path.extname(files.photo.name);
// 获取老路径
let oldPath = __dirname + "/" + files.photo.path;
// 设置新路径
let newPath = __dirname + "/uploadDir/" + name + extName;
// 重命名
fs.rename(oldPath, newPath, (err) => {
if (!err) {
res.writeHead(200,{'content-type': 'text/html; charset=utf-8'});
res.write("写入成功!");
res.write('received upload:\n\n');
res.end(util.inspect({fields: fields, files: files}));
} else {
throw err;
}
});
} else {
throw err;
}
});
}
}).listen(8080);

在 Node.js 回调函数中, 一定要对 err 进行判断, 否则有时候错误令人莫名其妙。


测试

这个时候先运行服务器
运行服务器

然后前端表单提交上传文件
运行服务器

服务器响应到前端
运行服务器

上传的文件会在项目文件 uploadDir 目录下


总结

  1. 创建Formidable.IncomingForm对象, 实例化一个传入表单 let form = new formidable.IncomingForm();
  2. form.uploadDir = "/my/dir"; 设置上传文件存放的文件夹,默认为系统的临时文件夹,可以使用fs.rename()来改变上传文件的存放位置和文件名
  3. form.maxFieldsSize = 2 * 1024 * 1024; 限制所有存储表单字段域的大小(除去file字段),如果超出,则会触发error事件,默认为2M
  4. form.encoding = "utf-8";设置表单域的编码
  5. form.parse(request, [callback]) 该方法会转换请求中所包含的表单数据
  6. 更多请前往 官方文档
------ 本文结束 ------

版权声明

Tflin's Blog by Tan Feng Lin is licensed under a Creative Commons BY-NC-ND 4.0 International License.
谭丰林创作并维护的Tflin's Blog博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证
本文首发于Tflin's Blog 博客( http://tflin.com ),版权所有,侵权必究。