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');
}
});
});
Related
The only way I can make Multer upload files is putting the multer logic in routes.js, but it doesn't seem right, since I'm using MVC.
The controller.js:
const upload_post = async (req, res, next) => { res.send('file uploaded') }
The routes.js:
router.post('/send', upload.single('image'), controller.upload_post)
The problem is, I have to put all Multer logic in routes.js file (upload, destination, storage, etc), otherwise it won't recognize the middleware upload.single. It works, but doesn't seem to be the best approach. So, is there a way to improve this code?
You can still keep all Multer upload logic inside your controller. Take a look at the Express Multer Error Handling section (at the bottom). It allows you to call the upload middleware programatically within other middleware, so you don't need to define it in your top route handler.
For example you would use this:
index.js
const express = require("express");
const multer = require("multer");
const bodyParser = require("body-parser");
const app = express();
const upload = multer().single("inputFile");
app.use(bodyParser.text({ type: "text/plain" }));
const OtherMiddleware = (req, res) => {
upload(req, res, function (err) {
if (!req.file) {
res.error({ status: 500, message: "foo", detail: "bar" });
} else if (err instanceof multer.MulterError) {
console.log(err);
// res.error({ status: 500, message: "multer", detail: "err" });
// A Multer error occurred when uploading.
} else if (err) {
console.log(err);
// An unknown error occurred when uploading.
}
// Everything went fine.
console.log("File Uploaded", req.file.originalname);
// send the file back to the server for example
res.send(req.file.buffer);
});
};
app.get("/", (req, res) => {
res.json({ visit: "/upload" });
});
app.post("/upload", OtherMiddleware);
app.listen(3000, () => {
console.log("Multer Upload API is listening on port 3000");
});
Here's a working sandbox I created to demonstrate this functionality.
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);
});
This question already has an answer here:
AngularJS Upload Multiple Files with FormData API
(1 answer)
Closed 3 years ago.
Trying to upload a file from Angularjs UI to nodejs server but facing issues with bodyparser, adding limit to it throws -
"SyntaxError: Unexpected token - in JSON at position 0",
if limit not added throws -
"Payload too large"
I am using connect-multiparty middleware to upload the file. Tried with {limit: '50mb'} in bodyparser and without any limit as well.
UI Code -
$('#imgupload').on('change', function (evt) {
let uploadedFiles = evt.target.files;
let formData = new FormData();
for (var i = 0; i < uploadedFiles.length; i++) {
formData.append("uploads[]", uploadedFiles[i],
uploadedFiles[i].name);
}
let url = "/upload";
httpService.restApi(url,formData)
.then(function (response) {
console.log("the file has been uploaded to local server
",response);
});
});
Nodejs (server code)-
const multipart = require('connect-multiparty');
const multipartMiddleware = multipart({ uploadDir: './uploads' });
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({extended: true}));
app.post('/upload', multipartMiddleware, (req, res) => {
res.json({
'message': 'File uploaded succesfully.'
});
});
Remove the bodyParser middleware from the path:
const multipart = require('connect-multiparty');
const multipartMiddleware = multipart({ uploadDir: './uploads' });
̶a̶p̶p̶.̶u̶s̶e̶(̶b̶o̶d̶y̶P̶a̶r̶s̶e̶r̶.̶j̶s̶o̶n̶(̶{̶l̶i̶m̶i̶t̶:̶ ̶'̶5̶0̶m̶b̶'̶}̶)̶)̶;̶
̶a̶p̶p̶.̶u̶s̶e̶(̶b̶o̶d̶y̶P̶a̶r̶s̶e̶r̶.̶u̶r̶l̶e̶n̶c̶o̶d̶e̶d̶(̶{̶e̶x̶t̶e̶n̶d̶e̶d̶:̶ ̶t̶r̶u̶e̶}̶)̶)̶;̶
app.post('/upload', multipartMiddleware, (req, res) => {
res.json({
'message': 'File uploaded succesfully.'
});
});
The contents is application/form-data, not application/json or application/x-www-form-urlencoded.
If you use multer and make the API call using $http explicitly setting the "Content-Type" header to multipart/form-data you will get "Multer: Boundary not found error" and if you remove the "Content-Type" header or set it to false it throws - "Converting circular structure to JSON error". Hence i used "fetch" to make the API call as it automatically identified the "Content-Type".
UI Code (modified as below) -
$('#imgupload').unbind('change').on('change', function (evt) {
evt.stopPropagation();
evt.preventDefault();
let uploadedFiles = evt.target.files;
let formData = new FormData();
for(let i=0; i<uploadedFiles.length;i++){
formData.append("uploads", uploadedFiles[i], uploadedFiles[i].name);
}
let url = '/upload';
var req = {
method: 'POST',
body: formData
}
fetch(url,req).then(function(response) {
console.log("the file has been uploaded to local server ",response);
$scope.uploadToSftp();
});
});
Nodejs Code (modified as below) -
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads/')
},
filename: function (req, file, cb) {
cb(null, file.originalname)
}
})
const multipartMiddleware = multer({ storage: storage });
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({
extended: true
}));
app.post('/upload', multipartMiddleware.array("uploads",2), function(req, res)
{
console.log("the upload api is called");
return res.send(req.files);
});
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');
});