1. 程式人生 > >nodejs使用multer中介軟體上傳混合表單提交(檔案和非檔案普通文字域)

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)
...