nodejs使用multer中介軟體上傳混合表單提交(檔案和非檔案普通文字域)
我的一個表單包括幾個input(type=’text’)普通的文字域和input(type=’file’),兩者同屬一個表單,form設定enctype=’multipart/form-data’。需要混合上傳,savePoster是儲存檔案邏輯,save是儲存資訊(文字域提交過來的)邏輯
前端頁面(jade模板)最簡化後大致是這樣的:
form(method='post', action='/admin/movie/save' enctype='multipart/form-data')
input(type="file", name="uploadPoster")
input(type="text", name="movie[flash]", value=movie.flash)
我們在路由裡是這樣處理的
router.post('/admin/movie/save', User.signinRequired, User.permission, Movie.savePoster, Movie.save);
理想狀態:
檔案上傳和文字域同屬一個表單,並且表單設定enctype=’multipart/form-data’
在savePoster裡,獲取上傳的檔案通過第三方中介軟體比如multer,multiparty等,req.file(s)可以獲取到檔案
在save中,獲取表單中其他普通文字域的movie物件(包括movie[title], movie[director],movie[year]等等)可以通過body-parser的req.body.movie獲取
我的情況:
在設定enctype=’multipart/form-data’後,檔案能正常接收,可以在multer中介軟體提供的方法中用req.file獲取檔案資訊,列印req.file:
{ fieldname: 'uploadPoster',
originalname: 'b922270259dece707ef6c6a50259a406_r.png',
encoding: '7bit' ,
mimetype: 'image/png',
destination: 'public/uploads',
filename: 'uploadPoster_1494237299545.png',
path: 'public\\uploads\\uploadPoster_1494237299545.png',
size: 134815 }
但是save裡,req.body.movie._id獲取不到了,提示:
Cannot read property 'id' of undefined
分析:
1.我們把事情弄簡單點,不設定enctype=’multipart/form-data’,預設為application/x-www-form-urlencoded 描述:在傳送前編碼所有字元(預設)
點選提交按鈕瀏覽器請求是這樣的(通過Formdata):
savePoster方法裡獲取不到的req.file,因為表單已經被編碼了所有字元,uploadPoster只是一個檔名,不是一個檔案資料,save方法里正常獲取movie物件(包括movie[title], movie[director],movie[year]等等);
2.設定enctype=’multipart/form-data’
描述:不對字元編碼,在使用包含檔案上傳控制元件的表單時,必須使用該值。
點選提交按鈕瀏覽器請求是這樣的:
這裡普通文字域已經不被編碼傳送給後臺了,後臺savePoster中只能通過multer中介軟體提供的方法中用req.file獲取檔案,在save中通過req.body(我們要獲取的是req.body.movie對吧,獲取不到)獲取不到。這裡瀏覽器是通過Request.payload傳送普通文字域表單資料,是由boundary拼接而成(這裡又涉及到ff和chrome的差異了),總之我們用req.body指定是獲取不到了,在java中倒是可以通過inputstream獲取,這塊我也不是很瞭解。
解決辦法:
查了很多資料,終於自己想通了. multer支援在獲取檔案資料同時獲取表單域引數(果然看官方文件加上自己理解才是最好的)
var express = require('express')
var multer = require('multer')
var upload = multer({ dest: 'uploads/' })
var app = express()app.post('/profile', upload.single('avatar'), function (req, res, next) {
// req.file 是 name為`avatar` 檔案的資訊
// req.body 將具有文字域資料, 如果存在的話})
在movie.js中
/*savePoster*/
exports.savePoster = function(req, res, next) {
var singleFileUpload=multer.single('uploadPoster'); //設定上傳方式為單檔案上傳
singleFileUpload(req, res, function(err){
if (err) {
return console.log(err);
}
req.body = req.body;
//由於設定了enctype='multipart/form-data',我們在save方法裡取req.body是取不到值的,這裡使用multer的req.body能獲取文字域的值,將multer裡的req.body賦給當前的req.body,並next傳給save方法
console.log(req.file);
next();
});
}
視訊裡面是直接在savePoster裡配置multipart,我用的是multer,singleFileUpload=multer..是我引入的multer的配置檔案,裡面包括了multer中介軟體和設定好的引數(目錄,檔案大小限制等),所以我在這裡可以直接用req.file獲取接收到的檔案(包括req.file.originalName等等).看程式碼裡面的註釋差不多解析清楚了。
exports.save = function(req, res) {
var movieObj = req.body.movie;
var id = movieObj._id;
...
結果:
成功在savePoster中獲取req.file,在save中獲取req.body.movie
後記:
我還傻逼兮兮的問能不能分開上傳,不放在一個表單裡上傳。好可恥。哦,對了,用formidable的同學可以嘗試下,它也自帶解析普通文字域的能力
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
//do something with files or req.body(if its exists)
...