Multer 是一个 node.js 中间件,用于处理 multipart/form-data
类型的表单数据,它主要用于上传文件。
注意: Multer 不会处理任何非 multipart/form-data
类型的表单数据。
npm i multer --save
Multer 会在express 的 request
对象里添加一个 body
对象 (包含表单的文本域信息)以及 file
或 files
对象 (单文件通过req.file获取,多文件通过req.files获取,file
或 files
对象包含对象表单上传的文件信息)。
//1.引入依赖
const express = require('express')
const multer = require('multer')
//存储在uploads文件夹下面,没有会直接创建
const upload = multer({ dest: 'uploads/' })
const app = express()
//相同的
//以下fielname(字符串)必须前端传过来的myformData对象的属性名一致,不然后端接收不到这个文件对象 // req.body 将具有文本域数据,如果存在的话
app.post('/profile', upload.single(fielname), function (req, res, next) {
//multer实例.single(fielname)——单文件上传,文件对象获取通过req.file
//multer实例.single(fieldame)——接受一个以fielname命名的文件,这个文件的信息保存在req.file中的fielname属性
// req.body 将具有文本域数据,如果存在的话
})
app.post('/photos/upload', upload.array(fielname, 12), function (req, res, next) {
//multer实例.array(fielname[,maxCount])——接收一个以fielname命名的文件数组;maxCount——限制上传的最大数量,这些文件的信息保存在req.files里面
})
const cpUpload = upload.fields([{ name:fields[, maxCount: 1 ]}, { name: 'gallery', maxCount: 8 }])
//multer实例.files(fields)——接受指定fields的混合文件,获取——req.files
//fields应该是一个对象数组,应该具有name和可选的maxCount属性
app.post('/profile', upload.none(), function (req, res, next) {
//multer实例.none()——只接受文本域
})
后端接收到的文件对象,文件对象的接收见上一步
每个文件具有下面的信息
Key | Description | Note |
---|---|---|
fieldname |
Field name 由表单指定(前后端一致) | |
originalname |
用户计算机上的文件的名称 | |
encoding |
文件编码 | |
mimetype |
文件的 MIME 类型 | |
size |
文件大小(字节单位) | |
destination |
保存路径 | DiskStorage |
filename |
保存在 destination 中的文件名 |
DiskStorage |
path |
已上传文件的完整路径 | DiskStorage |
buffer |
一个存放了整个文件的 Buffer |
MemoryStorage |
Multer 接受一个 options 对象,其中最基本的是 dest
属性(一般情况下使用dest就可以了),这将告诉 Multer 将上传文件保存在哪。如果你省略 options 对象,这些文件将保存在内存中,永远不会写入磁盘。
为了避免命名冲突,Multer 会修改上传的文件名。这个重命名功能可以根据您的需要定制。
重命名可以通过new Date().getTime()拼接保证不重复。
如:file.filename.slice(0, 10) + new Date().getTime() + '.' + hou
(hou——文件后缀名的截取,也可以自己配置)
以下是可以传递给 Multer 的选项。
Key | Description |
---|---|
dest or storage |
在哪里存储文件 |
fileFilter |
文件过滤器,控制哪些文件可以被接受 |
limits |
限制上传的数据 |
preservePath |
保存包含文件名的完整文件路径 |
加强对文件上传的更多控制
磁盘存储引擎可以让你控制文件的存储
```javascript
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '/tmp/my-uploads')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
const upload = multer({ storage: storage })
有两个选项可用,destination
和 filename
。他们都是用来确定文件存储位置的函数。
destination
是用来确定上传的文件应该存储在哪个文件夹中。也可以提供一个 string
(例如 '/tmp/uploads'
)。如果没有设置 destination
,则使用操作系统默认的临时文件夹。
注意: 如果你提供的 destination
是一个函数,你需要负责创建文件夹。当提供一个字符串,multer 将确保这个文件夹是你创建的。
filename
用于确定文件夹中的文件名的确定。 如果没有设置 filename
,每个文件将设置为一个随机文件名,并且是没有扩展名的。
注意:Multer 不会为你添加任何扩展名,你的程序应该返回一个完整的文件名。
每个函数都传递了请求对象 (req
) 和一些关于这个文件的信息 (file
),有助于你的决定。
注意 req.body
可能还没有完全 填充,这取决于向客户端发送字段和文件到服务器的顺序
内存存储引擎将文件存储在内存中的 Buffer
对象,它没有任何选项。
const storage = multer.memoryStorage()
const upload = multer({ storage: storage })
当使用内存存储引擎,文件信息将包含一个 buffer
字段,里面包含了整个文件数据。
警告: 当你使用内存存储,上传非常大的文件,或者非常多的小文件,会导致你的应用程序内存溢出。
设置一个函数来控制什么文件可以上传以及什么文件应该跳过,这个函数应该看起来像这样:
```javascript
function fileFilter (req, file, cb) {
// 这个函数应该调用 `cb` 用boolean值来
// 指示是否应接受该文件
// 拒绝这个文件,使用`false`,像这样:
cb(null, false)
// 接受这个文件,使用`true`,像这样:
cb(null, true)
// 如果有问题,你可以总是这样发送一个错误:
cb(new Error('I don\'t have a clue!'))
}
一个对象,指定一些数据大小的限制。
Key | Description | Default |
---|---|---|
fieldNameSize |
field 名字最大长度 | 100 bytes |
fieldSize |
field 值的最大长度 | 1MB |
fields |
非文件 field 的最大数量 | 无限 |
fileSize |
在 multipart 表单中,文件最大长度 (字节单位) | 无限 |
files |
在 multipart 表单中,文件最大数量 | 无限 |
parts |
在 multipart 表单中,part 传输的最大数量(fields + files) | 无限 |
headerPairs |
在 multipart 表单中,键值对最大组数 | 2000 |
当遇到一个错误,multer 将会把错误发送给 express。
如果你想捕捉 multer 发出的错误,你可以自己调用中间件程序。如果你想捕捉multer错误,你可以使用 multer
对象下的 MulterError
类 (即 err instanceof multer.MulterError
)。
const multer = require('multer')
const upload = multer().single('avatar')
app.post('/profile', function (req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
// 发生错误
} else if (err) {
// 发生错误
}
})
})
app.js基本配置
// 1.引入
const express = require('express')
const morgan = require('morgan')
const favicon = require('serve-favicon')
const path = require('path')
const router = require('./router/router')
// 2.创建服务器
const app = express()
//3.开放目录
app.use(express.static(__dirname + '/public'))
// 开发日志
// app.use(morgan('dev'))
// 网页图标
app.use(favicon(path.join(__dirname, 'public', 'img', '1.jpg')))
// body请求(express4以上内置的)
app.use(express.urlencoded({ 'limit': '10mb', 'extended': true }))
app.use(express.json({ 'limit': '10mb' }))
app.use("/api", router)
// 监听
app.listen(8000, (req, res) => {
console.log('服务已经运行\nhttp://127.0.0.1:8000\nhttp://localhost:8000');
})
router.js
// 1.引入
const express = require('express')
const multer = require('multer')
const path = require('path') //内置的路径模块
// 创建router
const router = express.Router()
// 设置保存路径和文件名
const storage = multer.diskStorage({
destination: function(req, res, cb) {
// 设置文件存储路径
cb(null, './public/uploads')
},
filename: function(req, file, cb) {
// 设置文件名
// Math.round(Math.random() *1E9)是生成一个整数部分9位数的随机数,再取整
let fileData = Date.now() + "-" + Math.round(Math.random() * 1e9) + path.extname(file.originalname);
cb(null, file.fieldname + "-" + fileData)
}
})
const upload = multer({
storage: storage
})
// 配置路由
router.post('/upload', upload.single('file'), (req, res) => {
console.log(req.file);
console.log(req.body);
// 随便响应下
res.json({
code: 0,
msg: "ok"
})
})
// 导出router
module.exports = router
前端
html
<body>
<form id="xcgg_yyds">
<input type="file" id="xcgg" accept=".png,.jpeg,.jpg,.gif">
form>
body>
js
$('#xcgg').on('change', function() {
console.log(this.files);
let fileObject = this.files[0]
let myFormData = new FormData()
myFormData.append('file', fileObject)
//随便传个看看
myFormData.append('name', 'xcgg')
$.ajax({
url: 'http://127.0.0.1:8000/api/upload',
type: 'post',
contentType: false,
processData: false,
data: myFormData,
success(res) {
console.log(res);
}
})
})
html
<body>
<form id="xcgg_yyds">
<input type="file" id="xcgg" accept=".png,.jpeg,.jpg,.gif" multiple>
form>
body>
js
$('#xcgg').on('change', function() {
let fileObjects = this.files
let myFormData = new FormData()
myFormData.append('files', fileObjects)
//这里需要依次添加进去
for (let i = 0; i < fileObjects.length; i++) {
myFormData.append('files', fileObjects[i])
}
myFormData.append('name', 'xcgg')
$.ajax({
url: 'http://127.0.0.1:8000/api/upload',
type: 'post',
contentType: false,
processData: false,
data: myFormData,
success(res) {
console.log(res);
}
})
})
后端
拦截请求不同,其他都相同
router.post('/upload', upload.array('files', 3), function(req, res, next) {
let files = req.files //多文件的获取
console.log(req.files);
if (files.length === 0) {
res.json({
code: 500,
msg: '文件上传不能为空'
})
return
} else {
res.json({
code: 0,
msg: '上传成功',
data: files
})
}
})