Reputation: 1249
I'm trying to get multer working on my backend to be able to handle files. I'm sending (POST) from an Angular 9+ Front-End a FormData Object to my backend that looks like:
foo: 'test',
bar: { id: 'someId', name: 'someName' },
file: { SOMEFILE }
On my back-end I've got a server.js file:
const path = require('path');
const express = require("express");
const cors = require('cors');
const bodyParser = require("body-parser");
const mongoose = require('mongoose');
const app = express();
// SOME ROUTE IMPORTS -- Just the variable pointing to the route.
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
mongoose.connect(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
useCreateIndex: true
});
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB Connection Error:'));
// THERE I "USE" THE PREVIOUSLY IMPORTED ROUTES: app.use(routeX);
and the route file in question (let's call it routeX):
const express = require('express');
const router = express.Router();
const multer = require('multer');
const MIME_TYPE_MAP = {
'image/png': 'png',
'image/jpeg': 'jpeg',
'image/jpg': 'jpg',
'application/pdf': 'pdf'
}
const storage = multer.diskStorage({
destination: (req, file, cb) => {
const isValid = MIME_TYPE_MAP[file.mimetype];
let error = new Error("Invalid mimeType");
if (isValid) {
error = null;
}
cb(error, "docLibrary/equipment/");
},
filename: (req, file, cb) => {
const dateNow = Date.now();
const name = dateNow + '-' + file.originalname.toLowerCase().split(' ').join('_') + '-' + file.fieldname;
const ext = MIME_TYPE_MAP[file.mimetype];
cb(null,name + '.' + ext)
}
});
const upload = multer({storage: storage});
const cpUpload = upload.fields([{name: 'proforma', maxCount: 1}, {name: 'technicalDetails', maxCount: 1}]);
router.post('/newEquipment', cpUpload, async (req, res, next) => {
const url = req.protocol + '://' + req.get('host') + '/';
let receivedEquip = req.body;
console.log('receivedEquip\n');
console.log(JSON.stringify(receivedEquip));
console.log('\n');
}
The problem is that the console.log functions gives me the following JSON for req.body:
"foo": 'test',
"bar": "[object Object]",
"bar" has become for some reason a String object that specifies the type of its value... I get the feeling that it's because of multer and the fact that I'm sending a FormData but I'm not sure where to look at here... Any ideas ?
Upvotes: 0
Views: 3701
Reputation: 1249
Alright, so it was pretty dumb afterall... I'm not familiar with the FormData Object and by this point I wish there was a better way to send blob to the back end. Not sure it exists, so for the moment, the workaround goes like this:
ORIGIN OF THE PROBLEM:
As mentioned by our good friend VSCode - a FormData
doesn't accept an object as a value. Only Blobs
and strings
:
Argument of type '{ test: string; secondTest: string; }' is not assignable to parameter of type 'string | Blob'.
Object literal may only specify known properties, and 'test' does not exist in type 'Blob'.ts(2345)
SOLUTION
So all you have to do when appending to the FormData
is stringify your JSON Object
:
if (typeof currFormValue[key] === 'object') {
const currObject = JSON.stringify(currFormValue[key])
someFormDataObject.append(key, currObject);
}
and on the server parse agains these fields to JSON
like so:
Object.keys(req.body).map(reqKey => { if (reqKey === 'knowObject') { JSON.parse(req.body[reqKey]); } }
I'm sure there's a much better structure to work with out there but I know better than asking on SO which structure to use ;)
Thanks all!
Upvotes: 2
Reputation: 167
Ok, try doing this;
router.post('/newEquipment', cpUpload, async (req, res, next) => {
const url = req.protocol + '://' + req.get('host') + '/';
let receivedEquip = JSON.parse(JSON.stringify(req.body));
let uploadFile = req.files.file // to get the file from the formData
console.log('receivedEquip\n');
console.log(receivedEquip);
console.log('\n');
});
Upvotes: 1