问题
I'm trying to use multer to support file upload from my react app client to my node/express backend.
For some background, I've been using postman 6.7.2, node 8.11.1, express 4.16.3, and multer 1.4.1. After following a tutorial, I could use postman to log req.body and see the entries in the formdata; as in I can log the req.body and the req.file in the route handler () and the file even saves. But when I try sending a request from my react app, the req.body logs as {} and the req.file logs as undefined.
This is my app.js file.
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config();
}
const path = require('path');
const port = process.env.PORT || 4000;
const mongoURI =
process.env.NODE_ENV !== 'production'
? 'mongodb://localhost/mern2'
: process.env.MONGODB_URI;
// node server dependencies
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const app = express();
const routes = require('./controllers/index');
// Server setup for MongoDB
mongoose.connect(mongoURI);
mongoose.set('useCreateIndex', true);
mongoose.Promise = global.Promise;
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// use router handlers
app.use('/api', routes);
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
// res.render('error');
});
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});
app.listen(port, () =>
console.log('LISTENING FOR REQUESTS ON PORT ' + port)
);
this is my routes file (controllers/index.js)
const express = require('express')
, router = express.Router();
router.use('/admin', require('./admin'));
module.exports = router;
and this is my route handler, (admin.js)
router.patch('/panel', upload.single('panelImage'), (req, res) => {
console.log('req.headers', req.headers);
console.log('req.body', req.body);
console.log('req.file', req.file);
res.send({message: 'success'});
});
on the react side, i have a function patchFileReq, which is like a normal patch request accept it adds the multipart form data header and sends a FormData object as the body.
backendUtils.js
export function patchFileReq (url, body, token) {
var formData = new FormData();
Object.keys(body).forEach((key) => {
if(body[key] instanceof File){
formData.append(key, body[key], body[key].name);
}
else {
formData.append(key, body[key]);
}
});
for (var key of formData.entries()) {
console.log(key[0] + ' ' + key[1]);
}
const headers = prepareFormDataHeaders(prepareAuthorizationHeaders(token));
return Observable.ajax.patch(url, formData, headers)
.map(parseAjaxResponse)
.catch(parseAjaxError);
}
here's the output of admin.js when I use postman:
*** file *** { fieldname: 'testImage',
originalname: 'bsn_2R_80_test.png',
encoding: '7bit',
mimetype: 'image/png' }
req.headers { 'content-type': 'multipart/form-data; boundary=--------------------------069393334143100418462268',
'cache-control': 'no-cache',
'postman-token': 'd289023d-956e-4d03-a0b4-073e6ffbd346',
'user-agent': 'PostmanRuntime/7.6.0',
accept: '*/*',
host: 'localhost:4000',
'accept-encoding': 'gzip, deflate',
'content-length': '332255',
connection: 'keep-alive' }
req.body { testfield: 'testvalue' }
req.file { fieldname: 'testImage',
originalname: 'bsn_2R_80_test.png',
encoding: '7bit',
mimetype: 'image/png',
destination: './uploads',
filename: 'bsn_2R_80_test.png',
path: 'uploads/bsn_2R_80_test.png',
size: 331918 }
And here's the output for admin.js when I use my react app
req.headers { 'x-forwarded-host': 'localhost:3000',
'x-forwarded-proto': 'http',
'x-forwarded-port': '3000',
'x-forwarded-for': '127.0.0.1',
'accept-language': 'en-US,en;q=0.9,pl-PL;q=0.8,pl;q=0.7',
'accept-encoding': 'gzip, deflate, br',
referer: 'http://localhost:3000/admin-panel/doors-and-sidelites',
accept: '*/*',
'content-type': 'multipart/form-data; boundary=--------------------------833495845447745121323852',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36',
'x-requested-with': 'XMLHttpRequest',
origin: 'http://localhost:4000',
authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVjYzEzMGI5MmM2MjVmM2ZmMWRlNjk3OCIsImVtYWlsIjoiZ3JlZ29yeS50ZXJsZWNraUBnbWFpbC5jb20iLCJpYXQiOjE1NjIyNjc5MTgsImV4cCI6MTU2MjM1NDMxOH0.pb9GfYjiwQRK8TInsbhf1565ENnSBnsoELClLH1SXB0',
'content-length': '372484',
connection: 'close',
host: 'localhost:4000' }
req.body {}
req.file undefined
Just above, where it says
req.body {}
req.file undefined
Is where I'm not seeing what I expect; I would expect to see the same result for req.body and req.file as when I use postman, which can somehow pick up the body and file.
Edit: i'm adding more front end code.
epics/admin.js this is my epic middleware
...
import {adminSavePanelRequest} from '../../lib/backend;
const adminSavePanelSuccess = (payload) => ({payload, type: ADMIN_SAVE_PANEL_SUCCESS});
const adminSavePanelFailure = (payload) => ({payload, type: ADMIN_SAVE_PANEL_FAILURE});
const adminSavePanel = (action$, store) =>
action$.ofType(ADMIN_SAVE_PANEL)
.flatMap(() => {
const state = store.getState();
var serverPayload = {};
serverPayload = state.getIn(['adminPanel', 'doorsForm']).toJS();
return adminSavePanelRequest(serverPayload)
.map(adminSavePanelSuccess)
.catch((error) => Rx.Observable.of(error)
.do((error) => {
console.log(`Error: ${error}`);
})
.map(adminSavePanelFailure)
)
});
...
backend.js
import patchFileReq from './backendUtils';
export function adminSavePanelRequest(payload){
return patchFileReq('/api/admin/panel', payload, getAuthToken());
}
reducers/admin/model.js
export const DoorsForm = new Record({
_id: '',
name: '',
material: '',
testImage: ''
});
来源:https://stackoverflow.com/questions/56893435/multer-parses-multipart-form-data-when-using-postman-but-doesnt-parse-multipar