express如何使用formidable解析post表单?接收文件上传
发布于 作者:苏南大叔 来源:程序如此灵动~上一篇文章里面,苏南大叔描述了express
的body-parser
中间件。它有个致命的问题,就是不支持带文件上传的multipart/form-data
类型。而且在官方文档里面,推荐了其他的中间件来支持multipart/form-data
类型,其中就有本文要描述的formidable
中间件。那么,这个formidable
好用么?这就是本文要讨论的内容。
苏南大叔的程序如此灵动博客,记录苏南大叔和计算机代码的故事。测试环境:node@16.14.2
,express@4.18.2
,formidable@3.2.5
。本文代码基于commonjs
写法,而不是es module
。
v2
还是v3
苏南大叔个人觉得这个formidable
并不好用,无论是v2
还是v3
,都不好用,强烈不推荐使用。
formidable
官方git
地址如下:
formidable
目前市面上存在着两个版本,v2
和v3
。其中,v2
是默认版本,v3
是最新版本。
目前下面的命令安装的是v2
:
npm i formidable
读者看到的时候,默认安装的formidable
不一定是v2
了。
v2
安装方式是:
npm i formidable@v2
v3
安装方式是:
npm i formidable@v3
存在的问题
个人建议直接使用v3
版本:
npm uninstall formidable
npm install formidable@v3
【formidable@v2
】可以使用require
,也可以使用import
。其最大问题是:对于表单内同name
的输入项,处理错误,会造成覆盖的现象。正确的处理方式是处理为一个数组。
【formidable@v3
】,虽然解决了上述问题,但是只能import
。其存在的问题是:表单中对空的file
组件,处理不当,会出现0kb
的文件。
let form = new formidable.IncomingForm({
allowEmptyFiles: false,
minFileSize: 1,
});
这两个配置组合木有啥意义的。
v3
解决方案
苏南大叔既然推荐使用v3
版本,必然是解决了这两个大问题。只能import
的问题,解决方案是:
(async () => {
let formidable = await import('formidable');
//..
})();
对于0kb
上传文件的问题,苏南大叔修改了一下源码:
修改文件\node_modules\formidable\src\Formidable.js
的line360
:
删除错误提示代码:
if (!this.options.allowEmptyFiles && fileSize === 0) {
this._error(
new FormidableError(
`options.allowEmptyFiles is false, file size should be greather than 0`,
errors.noEmptyFiles,
400,
),
);
return;
}
增加新的代码:
import fs from 'node:fs';
if (fileSize <= 0) {
file.end(() => {
this._flushing -= 1;
// this.emit('file', part.name, file);
this._maybeEnd();
fs.rmSync(filepath);
});
return;
}
个人认为本来就不应该“上传”个0kb
的文件,这本来就有问题。不知道官方为什么试图诱导大家认为空文件是正常的。这里不多说,官方issue
里面由很多关于这个的讨论,似乎是得到解决了。但是,苏南大叔这里的最新版本仍然存在这个问题。
官方的关于空文件的配置,怎么改都不对。典型错误信息如下:
options.allowEmptyFiles is false, file size should be greather than 0
options.minFileSize (1 bytes) inferior, received 0 bytes of file data
基础代码
var express = require("express");
var app = express();
var server = app.listen(3222, function () {
var port = server.address().port;
console.log(`访问地址为 http://localhost:${port}`);
});
app.use(express.static('wwwroot'));
function generateFilename(oldFilename) {
let d = new Date();
let names = oldFilename.split(".");
return `${names[0]}_${"" + d.getFullYear() + (d.getMonth() + 1) + d.getDate() + '_' + d.getHours() + d.getMinutes() + d.getSeconds()}.${names[1]}`;
}
let path = require("path");
let fs = require('fs');
下面的文件上传,需要个临时目录。先创建一下临时的目录./tmp/
。
使用方式一【推荐】
app.post('/form2.php', (req, resp) => {
(async () => {
let formidable = await import('formidable');
let form = new formidable.IncomingForm({
// allowEmptyFiles: false,
// minFileSize: 1,
uploadDir: "./tmp", //文件临时上传路径
});
form.allowEmptyFiles = false;
form.parse(req, function (err, fields, files) {
console.log(files);
Object.values(files).forEach((file) => {
var size = file[0].size;
var oldpath = file[0].filepath;
if (size > 0) {
var newname = file[0].originalFilename;
console.log(file, oldpath, newname);
var newpath = path.join(path.dirname(oldpath), generateFilename(newname));
filepath = file.filepath;
fs.renameSync(oldpath, newpath, (err) => {
if (err) {
console.log(err);
}
})
}
else {
fs.rmSync(oldpath);
}
})
resp.send(JSON.stringify(fields));
});
})();
});
使用方式二【不推荐】
不推荐的原因,是这个定制化的版本蛮复杂的。
app.post('/form3.php', (req, resp) => {
(async () => {
let formidable = await import('formidable');
let form = new formidable.IncomingForm({
uploadDir: "./tempdir", // 会被下面的代码覆盖
});
var post = {}, files = {};
form.uploadDir = path.join(process.cwd() + "/tmp/");
form.on('error', function (err) {
console.log(err);
}).on('field', function (field, value) {
if (field in post) {
if (Array.isArray(post[field]) === false) {
post[field] = [post[field]];
}
post[field].push(value);
return;
}
post[field] = value;
}).on('file', function (name, file) {
files[name] = file;
filepath = file.filepath;
filename = file.originalFilename;
fs.rename(filepath, form.uploadDir + generateFilename(filename), err => {
if (err) {
console.log(err);
} else {
console.log("重命名成功!");
}
});
}).on('end', function () {
req.body = post;
resp.send(JSON.stringify(req.body));
});
form.parse(req);
})();
});
html
表单
这里配合上述代码的表单,如下所示:
<form action="form2.php" method="post" enctype="multipart/form-data">
<input type="text" name="username" value="苏南大叔multipart">
<input type="checkbox" id="a3" name="skill" value="nodejs" checked="checked">nodejs
<input type="checkbox" id="a4" name="skill" value="php" checked="checked">php
<input type="file" name="file" />
<input type="submit" value="Submit!">
</form>
<form action="form3.php" method="post" enctype="multipart/form-data">
<input type="text" name="username" value="苏南大叔multipart2">
<input type="checkbox" id="a3" name="skill" value="nodejs" checked="checked">nodejs
<input type="checkbox" id="a4" name="skill" value="php" checked="checked">php
<input type="file" name="file" />
<input type="submit" value="Submit!">
</form>
结束语
总的来说,本文就演示了formidable
在express
代码中的使用。文件上传方面,没有对文件进行过滤及合法性检查,留在后续文章中解决这个问题。更多express
文章,请参考:
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。