The problem is when I use multer and send a request in Postman the req.body comes as an empty object and the req.file comes as undefined. I've unchecked the content-type header in postman.
And here's the code:
//Route
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '../uploads/');
},
filename: function (req, file, cb) {
cb(null, new Date().toISOString() + file.originalname);
}
});
const upload = multer({
storage,
limits: {fileSize: 1024 * 1024 * 10}
});
router.post('/test', upload.single('profilePicture'), authController.test);
//Controller
const test = (req, res) => {
console.log(req.body)
console.log(req.files)
res.json({body:req.body, files:req.files})
}
//app.js
app.use(express.json({extended: true, limit: '30mb'}));
app.use(express.urlencoded({extended: true, limit: '30mb'}))
app.use(cookieParser());
app.use('/api/auth', authRoutes);
app.use('/api/product', productRoutes);
app.use('/api/profile', profileRoutes);
Edit: turnes out, the problem is in Postman. I made a request with axios from a React app and everything works. So the question is, why doesn't it work in Postman? Is it some Bug in software or is there some settings that we're supposed to change?
The problem is that Nodejs is by default uses Ansynchornus Javascript. You need to use the async-await approach and try-catch-finally methods over conventional JS programming.
So your controller would look like -
//Route
router.post('/test', async (req, res, next)=>
{
try{
await upload.single('profilePicture')
next()
} catch(err){
console.log(err)
res.send('failed!')
},
authController.test);
//Controller
const test = async (req, res) => {
try{
console.log(req.body)
console.log(req.files)
res.json({body:req.body, files:req.files})
} catch(err){
console.log(err);
}
}
A late addition to the answer.
If you're trying to just access the uploaded image, then you should make use of the buffer.
var storage = multer.memoryStorage()
var upload = multer({ storage: storage })
The below is a rudimentary file upload utility. It handles React / Axios API POSTs satisfactorily. Files get uploaded to the ~uploads folder under root on the server. How does one add API DELETE handling capability to it? Envision a use case where a user uploads an attachment to a blog post and then deletes the attachment from the blog post. Have had issues locating an example.
var cors = require('cors');
var express = require('express');
var multer = require('multer')
var app = express();
app.use(cors());
// Parse JSON bodies (as sent by API clients)
app.use(express.json());
var storage = multer.diskStorage(
{
destination: function (req, file, cb)
{
cb(null, 'fileuploads');
},
filename: function (req, file, cb)
{
cb(null, file.originalname );
}
})
var upload = multer({ storage: storage }).array('file')
app.post('/upload',function(req, res)
{
upload(req, res, function (err)
{
if (err instanceof multer.MulterError)
{
return res.status(500).json(err)
}
else if (err)
{
return res.status(500).json(err)
}
return res.status(200).send(req.file)
})
});
app.listen(8001, function() {
console.log('App running on port 8001');
});
EDIT:
Modified app.delete(...) to the below, which successfully deletes the file but after about a minute throws this error in the console: [Error: ENOENT: no such file or directory, unlink '<PATH VALUE>']
app.delete('/writer/:file_to_delete', async (req, res) =>
{
const path = 'assets/uploads/' + req.params.targetFile;
console.log(path);
try
{
fs.unlink(path)
// NOTE: `fs.unlinkSync(path)` does the same thing
console.log('File deleted!');
return res.status(200);
}
catch(err)
{
console.error(err)
return res.status(500).json(err)
}
});
I found a way forward. The code is below but I'm happy to hear about better ways to do it:
// NOTE: `file_name` values are obfuscated and uniquely generated by the client.
// The actual file name is in data storage.
app.post('/delete/:file_name', (req, res) =>
{
const theFile = 'attachments/' + req.params.file_name;
var resultHandler = function(err) {
if(err) {
console.log("file delete failed", err);
return res.status(500).json(err)
}
console.log("file deleted");
return res.status(200).send({data: req.params.file_name + ' deleted'});
}
fs.unlinkSync(theFile, resultHandler);
});
I'm trying to upload an image in my server.
In the front-end I'm working with Angular.
The front-end is working fine, I only posted to show you how I'm passing the file to back-end!
component.html
<div fxLayout="column" fxLayoutAlign="center center">
<div>
<mat-form-field>
<ngx-mat-file-input placeholder="Only photos" [accept]="'.jpg, .jpeg, .png'" (change)="onChange($event)"></ngx-mat-file-input>
</mat-form-field>
</div>
<div>
<button mat-button (click)="onSubmit()">Send</button>
</div>
</div>
component.ts - functions
imagem: File;
constructor(private uploadService: UploadService) { }
onChange(event) {
this.imagem = event.target.files[0];
}
onSubmit() {
this.uploadService.upload(this.imagem);
}
upload.service.ts - functions
constructor(private http: HttpClient) { }
upload(file: File) {
const formData = new FormData();
formData.append('img', file, file.name);
this.http.post(environment.apiBaseUrl + '/upload', formData, {responseType: 'text'}).subscribe(
res => console.log('Done')
);
}
In the back-end I have this structure:
app.js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const rtsIndex = require('./routes/index.router');
var app = express();
// middleware
app.use(bodyParser.json());
app.use(cors());
app.use('/api', rtsIndex);
// start server
app.listen(3000, () => console.log('Port: 3000'));
index.router.js
const express = require('express');
const router = express.Router();
const ctrlUpload = require('../controllers/upload.controller');
router.post('/upload', ctrlUpload.send);
module.exports = router;
upload.controller.js
const express = require('express');
const multer = require('multer');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/');
},
filename: (req, file, cb) => {
cb(null, Date.now()+'-'+file.originalname);
}
});
const upload = multer({ storage });
module.exports.send = (req, res) => {
upload.single('img');
console.log(req.body, req.files);
res.send('ok');
}
I've tried to call the middleware inside the routing, but I don't think it's correctly and I didn't reach the goal. Algo, the upload is not one.
On server side I get: {} undefined as result, which probably means the multer is not treating the file.
On client side I get: Done.
So what am I doing wrong? And how can I make it works with this back end structure?
Express middlewares are designed to be installed at the routing level. Indeed, in the MVC model express programmers call controllers "routes" (personally I perefer to call them controllers instead of routes in my code). Separating controllers from routes (they both mean the same thing) doesn't really make sense when viewed from traditional MVC frameworks - but you can if you want.
To use multer as designed you need to do it in index.router.js:
index.router.js
const express = require('express');
const router = express.Router();
const multer = require('multer');
const ctrlUpload = require('../controllers/upload.controller');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/');
},
filename: (req, file, cb) => {
cb(null, Date.now()+'-'+file.originalname);
}
});
const upload = multer({ storage });
router.post('/upload', upload.single('img'), ctrlUpload.send);
module.exports = router;
Then you need to remove all the multer related code from upload.controller.js
You can however insist on doing it in upload.controller.js. The key here is to understand what middlewares are.
In Express, a middleware is a function with the prototype:
function (req, res, next) { // next is optional
// middleware logic
}
Yes, that's right. The code in your upload.controller.js file is a middleware. You are writing a middleware yourself that happens to be at the end of the middleware chain.
You see, Express only accepts middlewares. Express has nothing else. Routes are middlewares that happen to be at the end.
Express .use(), .get(), .post() and related methods accept an infinite number of arguments. The first is optionally a route specifier (but not necessary) and the rest of the arguments are middlewares. For example:
app.get('/foo',
(req, res, next) => {
// first middleware
next(); // next is what allows processing to continue
},
(req, res, next) => {
// second middleware
next();
},
(req, res, next) => {
res.send('hello'); // controller logic - a controller
// is just the last middleware
// Note: if you call next() instead of res.send() in a
// controller express will respond with a 500 internal
// server error status with whatever string you pass
// to next() as the error message.
}
);
Knowing this, we know what the function upload.single('img') returns. The function does not execute the middleware logic. Instead it returns the middleware function:
let middleware = upload.single('img');
// middleware is now a function with the prototype:
// (req, res, next) => {}
So to execute the middleware logic we have to call it (express would automatically call it as part of route processing, just like how it calls your controller function, but if we want to do it ourselves we can).
Here's what you need to do if you want to implement the middleware in upload.controller.js:
module.exports.send = (req, res, next) => {
upload.single('img')(req, res, () => {
// Remember, the middleware will call it's next function
// so we can inject our controller manually as the next()
console.log(req.body, req.files);
res.send('ok');
});
}
That's a lot to unpack. We can make the code easier to understand if we refactor it a little:
let middleware = upload.single('img');
module.exports.send = (req, res, next) => {
// Define the controller here to capture
// req and res in a closure:
let controller = () => {
console.log(req.body, req.files);
res.send('ok');
};
middleware(req, res, controller); // call the middleware with
// our controller as callback
}
But this is very non-standard and would be highly unexpected to an experienced Express.js programmer. I wouldn't do this even though it's possible. It also tightly couple the middleware with your controller completely negating the very flexible nature of Express middleware configuration system.
An example of a separated file of Multer Middleware based in the #slebetman answer
./middlewares/multer.js
const multer = require('multer')
const ErrorMessages = require('../constants/ErrorMessages')
function makeid (length) {
var result = ''
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
var charactersLength = characters.length
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength))
}
return result
}
const DIR = './uploads/'
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, DIR)
},
filename: (req, file, cb) => {
const fileName = file.originalname.toLowerCase().split(' ').join('-')
cb(null, makeid(16) + '_' + fileName)
}
})
const upload = multer({
storage: storage,
fileFilter: (req, file, cb) => {
if (file.mimetype === 'image/png' || file.mimetype === 'application/pdf') {
cb(null, true)
} else {
cb(null, false)
return cb(new Error('Only .png, .jpg, .mp4 and .jpeg format allowed!'))
}
}
})
module.exports.send = (req, res, next) => {
return upload.single('file')(req, res, () => {
// Remember, the middleware will call it's next function
// so we can inject our controller manually as the next()
if (!req.file) return res.json({ error: ErrorMessages.invalidFiletype })
next()
})
}
./routes.js
routes.post('/object', multer.send, ObjectController.createObject)
This avoids the status 500 for wrong filetype
Hope that helps someone :D
A working example of how you can use it in an expressjs handler
import multer from 'multer';
export default {
async upload(req: Request, res: Response, next: NextFunction) {
const middleware = upload.single('photo');
return middleware(req, res, () => {
try {
const file = req.file;
console.log('req.file', req.file);
if (!file) {
throw new ResourceValidationError('media-library', [
{
property: 'avatar',
constraints: {
isNotEmpty: 'avatar should not be empty',
},
},
]);
}
console.log('filename:', file.filename);
res.status(StatusCodes.OK).json({
status: { code: StatusCodes.OK, phrase: ReasonPhrases.OK },
});
} catch (error) {
next(error);
}
});
},
};
I've written a react app that makes use of the FilePond file uploader but am having trouble getting multer to accept the request from filepond, it always returns a 500 error.
If I use a standard file upload control the request is accepted by the server and the file uploads fine.
Does anyone have any thoughts on what might be causing this?
Thanks
This is my server.js code:
var express = require('express');
var app = express();
var multer = require('multer')
var cors = require('cors');
app.use(cors())
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public')
},
filename: function (req, file, cb) {
cb(null, Date.now() + '-' +file.originalname )
}
})
var upload = multer({ storage: storage }).array('file')
app.get('/',function(req,res){
return res.send('Hello Server')
})
app.post('/upload', function(req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
return res.status(500).json(err)
// A Multer error occurred when uploading.
} else if (err) {
return res.status(500).json(err)
// An unknown error occurred when uploading.
}
res.send([req.files[0].filename]);
return res.status(200).send(req.file)
// Everything went fine.
})
});
app.listen(8000, function() {
console.log('App running on port 8000');
});
I'm trying to upload a file to my express server. The client code looks like this:
axios.post('localhost:3030/upload/audio/', formData)
And in my express server:
App.use(bodyParser.urlencoded({ extended: true }));
App.use(bodyParser.json());
App.post('/upload/audio/', function uploadAudio(req, res) {
let quality = ['320', '128'];
let file = req.body;
console.log(file)
res.send('Frick')
}
However, even though the mp3 file is sent:
The req.body is empty when logged (note the empty object):
How can I get the formData (and file) in Express.js?
As #Tomalak said body-parser does not handle multipart bodies.
So you need to use some third party module I suggest use awesome module multer
I tried to do your code, hope it can help you
App.post('/upload/audio/', function uploadAudio(req, res) {
var storage = multer.diskStorage({
destination: tmpUploadsPath
});
var upload = multer({
storage: storage
}).any();
upload(req, res, function(err) {
if (err) {
console.log(err);
return res.end('Error');
} else {
console.log(req.body);
req.files.forEach(function(item) {
console.log(item);
// move your file to destination
});
res.end('File uploaded');
}
});
});