我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

上一篇文章里面,苏南大叔描述了expressbody-parser中间件。它有个致命的问题,就是不支持带文件上传的multipart/form-data类型。而且在官方文档里面,推荐了其他的中间件来支持multipart/form-data类型,其中就有本文要描述的formidable中间件。那么,这个formidable好用么?这就是本文要讨论的内容。

苏南大叔:express如何使用formidable解析post表单?接收文件上传 - formidable解析表单文件上传
express如何使用formidable解析post表单?接收文件上传(图9-1)

苏南大叔的程序如此灵动博客,记录苏南大叔和计算机代码的故事。测试环境:node@16.14.2express@4.18.2formidable@3.2.5。本文代码基于commonjs写法,而不是es module

v2还是v3

官方git地址如下:

苏南大叔:express如何使用formidable解析post表单?接收文件上传 - github-formidable
express如何使用formidable解析post表单?接收文件上传(图9-2)

formidable目前市面上存在着两个版本,v2v3。其中,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的输入项,处理错误,会造成覆盖的现象。正确的处理方式是处理为一个数组。

苏南大叔:express如何使用formidable解析post表单?接收文件上传 - checkbox--issue
express如何使用formidable解析post表单?接收文件上传(图9-3)

formidable@v3】,虽然解决了上述问题,但是只能import。其存在的问题是:表单中对空的file组件,处理不当,会出现0kb的文件。

let form = new formidable.IncomingForm({
    allowEmptyFiles: false,
    minFileSize: 1,
});

这两个配置组合木有啥意义的。

v3解决方案

苏南大叔既然推荐使用v3版本,必然是解决了这两个大问题。只能import的问题,解决方案是:

(async () => {
    let formidable = await import('formidable');
    //..
})();

苏南大叔:express如何使用formidable解析post表单?接收文件上传 - 使用方式
express如何使用formidable解析post表单?接收文件上传(图9-4)

对于0kb上传文件的问题,苏南大叔修改了一下源码:
修改文件\node_modules\formidable\src\Formidable.jsline360
删除错误提示代码:

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;
}

苏南大叔:express如何使用formidable解析post表单?接收文件上传 - filesize-rmsync
express如何使用formidable解析post表单?接收文件上传(图9-5)

增加新的代码:

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/

苏南大叔:express如何使用formidable解析post表单?接收文件上传 - code-1
express如何使用formidable解析post表单?接收文件上传(图9-6)

使用方式一【推荐】

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));
        });
    })();
});

苏南大叔:express如何使用formidable解析post表单?接收文件上传 - code-2
express如何使用formidable解析post表单?接收文件上传(图9-7)

使用方式二【不推荐】

不推荐的原因,是这个定制化的版本蛮复杂的。

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);
    })();
});

苏南大叔:express如何使用formidable解析post表单?接收文件上传 - code-3
express如何使用formidable解析post表单?接收文件上传(图9-8)

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>

苏南大叔:express如何使用formidable解析post表单?接收文件上传 - 运行截图
express如何使用formidable解析post表单?接收文件上传(图9-9)

结束语

总的来说,本文就演示了formidableexpress代码中的使用。文件上传方面,没有对文件进行过滤及合法性检查,留在后续文章中解决这个问题。更多express文章,请参考:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   express