前言
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
- 我們定義的路由