Express文件表单解析中间件 Multer简介

54次阅读

共计 6915 个字符,预计需要花费 18 分钟才能阅读完成。

前言
Express 中最常使用的 form 解析中间件就是 body-parser 了, 但是它明确表示不会支持 multipart/form-data 类型的表单.
所以在 body-parser 官方文档中提供了如下的几个支持 multipart/form-data 类型的中间件的链接, 或者只支持 multipart/form-data 解析的中间件链接.

名称 & 地址
周下载量
stars

busboy
426,278
1448

multipart
240,921
993

formidable
1,390,361
4735

multer
284,926
5860

统计截止到 2018 年 12 月 26 日
multer 依赖 busboy 所以所以 busboy 的实际直接下载数量应该要减少 28 万????.
什么是 multipart/form-data 类型的表单?
最直观的解释就是支持上传文件的 form 表单, 如果不使用 JavaScript 中创建的话, 显式的 html 声明如下:
<form action=”/profile” method=”post” enctype=”multipart/form-data”>
<input type=”file” name=”file”/>
<input type=”submit” value=”submit”>
</form>
上例子中的 <input type=”file” name=”file” > 在页面中的显示就是为一个按钮点击后可以进行文件选择.
其次 input 可以添加 multiple=”multiple” 属性, 这个时候打开的文件选择框会允许多选文件.
总结一下有如下几种关系:

一个 name 对应多个文件
一个 name 对应一个文件
多个 name 对应多个单个文件
多个 name 对应多个文件

顺便说一句 multer 有中文文档.
正文
特点

multer 只会解析 form 设置为 enctype=”multipart/form-data” 表单.
multer 可以定制存储引擎

multer 会将上传的信息以及内容挂载到 request 对象上

request.body 保存文本内容
request.file 保存单个文件信息以及对应内容 (内存存储模式)
request.files 保存多个文件信息以及对应的内容 (内存存储模式)

基本工作流程

创建一个 multer 实例
使用该实例上提供的不同方法获取不同功能的中间件
放入到对应的路由中

上传单个文件实例
引入 multer 和 Express
const
express = require(‘express’),
multer = require(‘multer’),
app = express();
传入配置参数
const upload = multer({dest:’/uploads’});
注意:dest 参数指定了文件输出的位置, 可以详细指定文件输出以及储存后面会将.
使用 multer 中间件传递单个文件
app.get(‘/’,(request,response)=>{

console.log(‘get.request.body’,request.body);
console.log(‘get.request.file’,request.file);
console.log(‘get.request.files’,request.files);

response.send(‘<form action=”/” enctype=”multipart/form-data” method=”post”>’+
‘<input type=”text” name=”title”><br>’+
‘<input type=”file” name=”upload” multiple=”multiple”><br>’+
‘<input type=”submit” value=”Upload”>’+
‘</form>’)

});

app.post(‘/’,upload.single(‘upload’),(request,response)=>{

console.log(‘post.request.body’,request.body);
console.log(‘post.request.file’,request.file);
console.log(‘post.request.files’,request.files);

response.redirect(‘/’);

});

app.listen(8888,()=>{

console.log(‘express 正在监听 8888 端口 ’);

});
这个例子中我们监听了根路径, 分别处理两种不同的请求方式, 针对 get 我们响应表单, 针对 post 我们接受上传的内容.
注意:upload.single(‘upload’) 意思是高速 multer 只接收 name 是 upload 的单个文件.
注意: 这个例子中 input 是可以进行多选的, 也就是说后端指定了文件数量为 1 但是页面依然上传了多个, 这个时候 multer 会报错.
注意:dest 指定的路径为 upload/ 会将文件保存到根路径下的 upload 文件夹中, 对于 windows 系统来说是在运行这个应用对应的盘符下例如 F:\uploads\
这个例子中我填写了一个文本内容, 同时上传了一个文件, 输出结果如下:
post.request.body {title: ‘hello world’}
post.request.file {fieldname: ‘upload’,
originalname: ‘ 硬盘坏道扫描及修复工具 Victoria.7z’,
encoding: ‘7bit’,
mimetype: ‘application/octet-stream’,
destination: ‘/uploads’,
filename: ‘6bdfc0df998d72e6232d60f790f47ef8’,
path: ‘\\uploads\\6bdfc0df998d72e6232d60f790f47ef8’,
size: 1033375 }
上传多个文件实例
const
express = require(‘express’),
multer = require(‘multer’),
app = express();

const upload = multer({dest:’/uploads’});

app.get(‘/’,(request,response)=>{

console.log(‘get.request.body’,request.body);
console.log(‘get.request.file’,request.file);
console.log(‘get.request.files’,request.files);

response.send(‘<form action=”/” enctype=”multipart/form-data” method=”post”>’+
‘<input type=”text” name=”title”><br>’+
‘<input type=”file” name=”upload”><br>’+ // 此处有两个相同 name 的 input
‘<input type=”file” name=”upload”><br>’+
‘<input type=”submit” value=”Upload”>’+
‘</form>’)

});

app.post(‘/’,upload.array(‘upload’),(request,response)=>{// 注意此处使用的中间件和上例中不同

console.log(‘post.request.body’,request.body);
console.log(‘post.request.file’,request.file);
console.log(‘post.request.files’,request.files);

response.redirect(‘/’);

});

app.listen(8888,()=>{

console.log(‘express 正在监听 8888 端口 ’);

});
在这个例子的表单中有两个同名的 name 都是文件类型, 这次使用 array 的方式来进行接受, 控制台输出内容如下:
post.request.body {title: ‘hello world’}
post.request.file undefined
post.request.files [{ fieldname: ‘upload’,
originalname: ‘ 硬盘坏道扫描及修复工具 Victoria.7z’,
encoding: ‘7bit’,
mimetype: ‘application/octet-stream’,
destination: ‘/uploads’,
filename: ’71ed2ac4299d43a30f5c13892f33e51b’,
path: ‘\\uploads\\71ed2ac4299d43a30f5c13892f33e51b’,
size: 1033375 },
{fieldname: ‘upload’,
originalname: ‘ 新建文本文档.txt’,
encoding: ‘7bit’,
mimetype: ‘text/plain’,
destination: ‘/uploads’,
filename: ‘190bde8fcdd08d57648ffb243607ed9d’,
path: ‘\\uploads\\190bde8fcdd08d57648ffb243607ed9d’,
size: 218 } ]
在上面的例子中删除掉一个 input, 将剩余的 input 添加 multiple 属性用于多选, 页面中在选择文件框中选择多个文件也是可以顺利通过.
其余的方法
除了上方提到的 multer.single 方法外还有其他的几种方法.

array(name:string,maxcount?:number) 根据 name 限制上传文件的最大个数
fields(fields:object) 自定义限制规则
none() 只保留文本信息
any() 允许任意类型通过, 文件数组将保存在 req.files

实际上它们都可以视为 fields 方法的包装, 源码如下:
Multer.prototype.single = function (name) {
return this._makeMiddleware([{name: name, maxCount: 1}], ‘VALUE’)
}

Multer.prototype.array = function (name, maxCount) {
return this._makeMiddleware([{name: name, maxCount: maxCount}], ‘ARRAY’)
}

Multer.prototype.fields = function (fields) {
return this._makeMiddleware(fields, ‘OBJECT’)
}

Multer.prototype.none = function () {
return this._makeMiddleware([], ‘NONE’)
}

Multer.prototype.any = function () {
function setup () {
return {
limits: this.limits,
preservePath: this.preservePath,
storage: this.storage,
fileFilter: this.fileFilter,
fileStrategy: ‘ARRAY’
}
}

return makeMiddleware(setup.bind(this))
}
常用 API 一览
来自中文文档:
multer(opts)
Multer 接受一个 options 对象,其中最基本的是 dest 属性,这将告诉 Multer 将上传文件保存在哪。如果你省略 options 对象,这些文件将保存在内存中,永远不会写入磁盘。
为了避免命名冲突,Multer 会修改上传的文件名。这个重命名功能可以根据您的需要定制。
以下是可以传递给 Multer 的选项。

Key
Description

dest or storage

在哪里存储文件

fileFilter
文件过滤器,控制哪些文件可以被接受

limits
限制上传的数据

preservePath
保存包含文件名的完整文件路径

fileFilter
设置一个函数来控制什么文件可以上传以及什么文件应该跳过,这个函数应该看起来像这样:
function fileFilter (req, file, cb) {

// 这个函数应该调用 `cb` 用 boolean 值来
// 指示是否应接受该文件

// 拒绝这个文件,使用 `false`,像这样:
cb(null, false)

// 接受这个文件,使用 `true`,像这样:
cb(null, true)

// 如果有问题,你可以总是这样发送一个错误:
cb(new Error(‘I don\’t have a clue!’))

}
错误处理机制
当遇到一个错误,multer 将会把错误发送给 express。你可以使用一个比较好的错误展示页 (express 标准方式)。
如果你想捕捉 multer 发出的错误,你可以自己调用中间件程序。如果你想捕捉 Multer 错误,你可以使用 multer 对象下的 MulterError 类 (即 err instanceof multer.MulterError)。
var multer = require(‘multer’)
var upload = multer().single(‘avatar’)

app.post(‘/profile’, function (req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
// 发生错误
} else if (err) {
// 发生错误
}

// 一切都好
})
})
磁盘存储引擎 (DiskStorage)
磁盘存储引擎可以让你控制文件的存储。
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, ‘/tmp/my-uploads’)
},
filename: function (req, file, cb) {
cb(null, file.fieldname + ‘-‘ + Date.now())
}
})

var upload = multer({storage: storage})
有两个选项可用,destination 和 filename。他们都是用来确定文件存储位置的函数。
destination 是用来确定上传的文件应该存储在哪个文件夹中。也可以提供一个 string (例如 ‘/tmp/uploads’)。如果没有设置 destination,则使用操作系统默认的临时文件夹。
注意: 如果你提供的 destination 是一个函数,你需要负责创建文件夹。当提供一个字符串,multer 将确保这个文件夹是你创建的。
filename 用于确定文件夹中的文件名的确定。如果没有设置 filename,每个文件将设置为一个随机文件名,并且是没有扩展名的。
注意: Multer 不会为你添加任何扩展名,你的程序应该返回一个完整的文件名。
每个函数都传递了请求对象 (req) 和一些关于这个文件的信息 (file),有助于你的决定。
注意 req.body 可能还没有完全填充,这取决于向客户端发送字段和文件到服务器的顺序。
内存存储引擎 (MemoryStorage)
内存存储引擎将文件存储在内存中的 Buffer 对象,它没有任何选项。
var storage = multer.memoryStorage()
var upload = multer({storage: storage})
当使用内存存储引擎,文件信息将包含一个 buffer 字段,里面包含了整个文件数据。
警告: 当你使用内存存储,上传非常大的文件,或者非常多的小文件,会导致你的应用程序内存溢出。
filefilter 和 filename 还有路由触发的顺序
const
express = require(‘express’),
multer = require(‘multer’),
app = express();

const storage = multer.diskStorage({
destination:__dirname, // 保存到当前目录
filename(request,file,callback){

console.log(‘filename:’,file);

callback(null,’newfilename’);// 修改上传的文件名称
}
});

const upload = multer({
dest:’/uploads’,
fileFilter(request,file,cb){

console.log(‘fileFilter:’,file);

cb(null,true);

},
limits:{
fileSize:100000 // 限制上传文件大小为 100000 字节
},
storage // 使用默认的储存器
});
最后触发的顺序为:

filefilter
filename
我们定义的路由

正文完
 0