Multer file upload doesn't work with swaggerExpress - node.js

I'm trying to upload multipart form data using multer. I'm using swagger express middleware for my APIs. Without swagger everything was working. But with swagger, file is not uploaded. There's no validation error, but it simply doesn't upload file. Here's some code that you may want to see:
app.js
SwaggerExpress.create(config, function(err, swaggerExpress) {
if (err) { throw err; }
swaggerExpress.register(app);
var port = 8850;
https.createServer(options, app).listen(port, function () {
console.log('Bus993 server started # %s!', port);
});
});
upload function
function uploadImage(req, res, multer){
console.log("here", req.files);
//ABOVE SHOWS A VALID FILE
var storage = multer.diskStorage({
destination: function (req, file, cb) {
console.log("fILE", file);
//THIS IS NOT PRINTED
cb(null, '../../public/images');
},
filename: function (req, file, cb) {
console.log(file);
//THIS IS NOT PRINTED
cb(null, file.originalname.replace(/[.]{1}[a-zA-Z]+$/, "") + '_' + moment().format('X') + getExtension(file));
}
});
var upload = multer({storage: storage, fileFilter: fileFilter}).single('imageFile');
upload(req, res, function (err) {
if (err) {
res.status(422).json(
{
status: "error",
data: {error: err.message},
message: "Image upload failed."
}
);
} else {
res.status(200).json(
{
status: "success",
data: "",
message: "Image uploaded successfully."
}
);
}
});
function fileFilter(req, file, cb) {
console.log("fILE", file);
if ((file.mimetype != 'image/jpeg' && file.mimetype != 'image/png' && file.mimetype != 'image/gif') || file.size > 8000) {
cb(new Error('Invalid file.'), false);
} else {
cb(null, true);
}
}
function getExtension(file) {
var res = '';
if (file.mimetype === 'image/jpeg') res = '.jpg';
if (file.mimetype === 'image/png') res = '.png';
if (file.mimetype === 'image/gif') res = '.gif';
return res;
}
}
So, the problem seems that file is undefined here since when I'm using swagger. Earlier it was fine. But now it returns status:success but image was never uploaded.
Am I doing something wrong?

swaggerExpress uploading a file local/s3 successful
following setups are:
1. in swagger file(yaml)
/s3/upload:
x-swagger-router-controller: s3/upload
post:
operationId: upload
tags:
- S3
consumes:
- multipart/form-data
parameters:
- in: formData
name: file
description: The file to upload
type: file
2.add extra middleware
SwaggerExpress.create(config, function(err, swaggerExpress) {
if (err) { throw err; }
// install middleware
app.use(SwaggerUi(swaggerExpress.runner.swagger));
// install extra middleware
app.use(function (req, res, next) {
if(req.file){
req.files = req.file
}else{
req.files = {}
}
next();
});
// install middleware
swaggerExpress.register(app);
console.log("Listening on port: "+ port)
app.listen(port);
});
3.controller using multer,multerS3 and aws-sdk
define middleware before controller
s3 object
const uploadFile = multer({
storage: multerS3({
s3: s3,
bucket: 'bucket_name',
metadata: function (req, file, cb) {
cb(null, {fieldName: file.fieldname});
},
key: function (req, file, cb) {
cb(null, file.originalname)
}
})
}).fields([{name: "file"}])
controller.upload = async function(req, res, next){
console.log("---------upload---------------");
try{
uploadFile(req, res, function (error) {
if (error) {
console.log(error);
res.json({
result: "Error",
status: false
})
}else{
console.log('File uploaded successfully.',req.files);
res.json({
result: req.files.file[0],
status: true
})
}
});
}catch(e){
res.json({
message: "Error"
})
}
}
enter image description here

Related

Saving Images React To Nodejs

I am trying to upload an image from my front end to the backend but it it doesn't send the image in the request
It says that the formdata is empty and it says that there's no image found, where is the problem and how can I fix this error?
Here is the code from the Frontend made in react:
const [userInfo, setuserInfo] = useState({
file:[],
filepreview:null,
});
const handleInputChange = (event) => {
setuserInfo({
...userInfo,
file:event.target.files[0],
filepreview:URL.createObjectURL(event.target.files[0]),
});
}
const [isSucces, setSuccess] = useState(null);
const submit = async () =>{
const formdata = new FormData();
formdata.append('avatar', userInfo.file);
console.log(formdata)
Axios.post("http://localhost:4000/imageupload", formdata,{
headers: { "Content-Type": "multipart/form-data" }
})
.then(res => { // then print response status
console.warn(res);
if(res.data.success === 1){
setSuccess("Image upload successfully");
}
})
}
The code of the Backend made in NodeJS:
const storage = multer.diskStorage({
destination: path.join(__dirname, './temp', 'uploads'),
filename: function (req, file, cb) {
// null as first argument means no error
cb(null, Date.now() + '-' + file.originalname )
}
})
app.post('/imageupload', async (req, res) => {
try {
// 'avatar' is the name of our file input field in the HTML form
let upload = multer({ storage: storage}).single('avatar');
upload(req, res, function(err) {
// req.file contains information of uploaded file
// req.body contains information of text fields
if (!req.file) {
return res.send('Please select an image to upload');
}
else if (err instanceof multer.MulterError) {
return res.send(err);
}
else if (err) {
return res.send(err);
}
const classifiedsadd = {
image: req.file.filename
};
res.send("ok")
});
}catch (err) {console.log(err)}
})
Edit:
Multer is essentially a nodejs router,i.e. a function that can be pipelined between your HTTP request and HTTP response.
I think that you should first make multer analyze your HTTP content and to actually populate the req.file before actually evaluate express parsers do their job.
const storage = multer.diskStorage({
destination: path.join(__dirname, './temp', 'uploads'),
filename: function (req, file, cb) {
// null as first argument means no error
cb(null, Date.now() + '-' + file.originalname )
}
})
let upload = multer({ storage: storage});
app.post('/imageupload', upload.single('avatar'), async (req, res) => {
try {
// 'avatar' is the name of our file input field in the HTML form
// req.file contains information of uploaded file
// req.body contains information of text fields
if (!req.file) {
return res.send('Please select an image to upload');
}
else if (err instanceof multer.MulterError) {
return res.send(err);
}
else if (err) {
return res.send(err);
}
const classifiedsadd = {
image: req.file.filename
};
res.send("ok")
}catch (err) {console.log(err)}
})
I am assuming that your upload code is working. Have you tried to read the HTTP request from your browser to see that the image has been correctly attached to the request?
Because probably the issue lies in the fact that you are not actually parsing the image.
const file = new File(userInfo.file, "avatar.png", {
type: 'image/png' // choose the appropriate
});
const formdata = new FormData();
formdata.append('avatar', file);
console.log(formdata)

uploading files from react to node js with multer

I want to upload files from the form data in react which is being posted by axios like this.
const addNewProduct = () => {
const newProduct = {
name: name,
cost: cost,
size: size,
color: color,
material: material,
discount: discount,
description: description,
category: category
};
const nulls = Object.values(newProduct).filter(p => p === null);
if(nulls.length === 0 && images.imageFiles) {
let productFormData = new FormData();
productFormData.append('productInfo', JSON.stringify(newProduct));
productFormData.append('productImages', images.imageFiles);
const addUrl = "http://localhost:8080/cpnl/addproduct";
axios({
method: "POST",
url: addUrl,
data: productFormData,
headers: { "Content-Type": "multipart/form-data" }
})
.then((response) => {
console.log(response.data.msg);
})
.catch((response) => {
console.error(response);
});
}else {
Notiflix.Notify.Warning("Check your inputs!");
console.log(nulls);
console.log("product: \n" + JSON.stringify(newProduct));
}
};
then I want to upload images with multer to images folder. this is my code:
const storage = multer.diskStorage({
destination: "./public/images",
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
const upload = multer({
storage: storage,
limits:{fileSize: 1000000},
fileFilter: function(req, file, cb){
checkFileType(file, cb);
}
}).array("productImages", 5);
function checkFileType(file, cb) {
// Allowed ext
const filetypes = /jpeg|jpg|png/;
// Check ext
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
// Check mime
const mimetype = filetypes.test(file.mimetype);
if(mimetype && extname){
return cb(null,true);
} else {
cb('Error: Images Only!');
}
}
//receive form data from front-end and add new product to database
router.post('/addproduct', async (req, res) => {
upload(req, res, (err) => {
if(err) {
res.status(400).json({
msg: err
});
} else {
if(req.files == undefined) {
res.status(400).json({
msg: "Error: No file selected! please contact the developer."
});
} else {
data = req.body.productInfo;
res.status(200).json({
msg: "Files uploaded!"
});
console.log( "images: " + req.files);
console.log("data" + data);
}
}
});
});
first problem: I'm getting image files inside req.body.productImages and not inside req.files
second problem: when I send the request node js throws me this error:
TypeError: upload is not a function
why everything is messed up!?
Edit: I restarted the server and now I'm getting data but the files are not being uploaded. no error is shown.
UPDATE: second problem fixed
First Problem : You have used .array("productImages", 5); in your upload function. use .array("files", 5); to get the file in req.files.
Second Problem : I guess there is some typo error in your code upload(req, res, (err)... there is one extra bracket it should be only upload(req,res,err )...

I want to upload a file locally then upload that file to S3. However Multer only allows one or the other at a time

My goal is the following:
I want to get user uploaded PDF, extract the Text from within that PDF, assign the text to an array object. Once this is done I want to upload that file to an S3 bucket. Right now I am able to to do the first part without much issue. The reason why I am doing the local upload in the first place is so I can do the text extraction from PDF. These methods work on their own. If i want to upload to S3 it will and the database gets populated with the link for me to show it on the front-end but the BulletinMetaText field does not get populated with the extract text.
What way should I go about doing this?
If I have not been clear please let me know what more I can provide..
Multer methods:
let uploadToS3 = multer({
storage: multerS3({
s3: s3,
acl: "private",
bucket: env == "production" ? "xxxx" : "xxxx",
// metadata: function(req, file, cb) {
// console.log(file);
// console.log(req.body);
// cb(null, Object.assign({}, req.body));
// },
key: function(req, file, cb) {
// console.log(file);
cb(null, `${new Date().getFullYear()}/${file.originalname}`);
}
})
}).array("files");
// }).any();
var tempStorage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, "client/v1.7/src/assets/pdf/");
},
// By default, multer removes file extensions so let's add them back
filename: function(req, file, cb) {
cb(null, `${file.originalname}`);
}
});
var uploadToLocal = multer({ storage: tempStorage }).array("files");
let delay = time => {
return new Promise((resolve, reject) => {
setTimeout(function() {
resolve("DONE");
}, time);
});
};
Node/express Backend
if (req.params.type === "files") {
uploadToLocal(req, res, function(err) {
if (err) {
next({
status: 500, //Server Error
statusMessage: "Error: Failed to save file ",
catchError: err //System error
});
} else {
//TODO
let {
bulletinID,
bulletinUUID,
bulletinActive,
bulletinType,
bulletinCode,
bulletinGroup,
bulletinEn,
bulletinFr,
bulletinTitleEn,
bulletinDescriptionEn,
bulletinTitleFr,
bulletinDescriptionFr,
metaStringTags,
bulletinPermission
} = req.body.model;
uploadToS3(req, res, function(err) {
console.log("upload2Req");
console.log(req.body);
let bulletinFileEn,
bulletinFileFr = "";
// console.log("req.files");
console.log(req.files);
req.files.forEach(file => {
if (file.originalname && file.originalname.includes("_en")) {
console.log("file");
console.log(file);
console.log(file.key);
bulletinFileEn = file.key;
}
if (file.originalname && file.originalname.includes("_fr")) {
bulletinFileFr = file.key;
}
});
console.log(bulletinFileEn);
console.log(bulletinFileFr);
extract(
"client/v1.7/src/assets/pdf/test.pdf",
// req.files.originalname,
{ splitPages: false },
function(err, text) {
if (err) {
console.log(err);
return;
}
console.log("text");
// console.log(text);
bulletinMetaText = text;
if (err) {
next({
status: 500, //Server Error
statusMessage: "Error: Failed to save file locally ",
catchError: err //System error
});
} else {
// !~
let newBulletin = {
bulletinID: bulletinID ? bulletinID : "NULL",
bulletinUUID: bulletinUUID ? bulletinUUID : uuid(),
bulletinActive: bulletinActive ? bulletinActive : true,
postedByUserID: req.user.id,
postedByUserUUID: req.user.userUUID,
bulletinType: bulletinType,
bulletinCode: bulletinCode,
bulletinGroup: bulletinGroup,
bulletinEn: bulletinEn ? true : false,
bulletinFr: bulletinFr ? true : false,
//English Bulletin - File/Link
bulletinTitleEn: bulletinTitleEn,
bulletinDescriptionEn: bulletinDescriptionEn,
bulletinLinkEn: null,
bulletinFileEn: bulletinFileEn,
//French Bulletin - File/Link
bulletinTitleFr: bulletinTitleFr,
bulletinDescriptionFr: bulletinDescriptionFr,
bulletinLinkFr: null,
bulletinFileFr: bulletinFileFr,
metaStringTags: metaStringTags,
bulletinPermission: bulletinPermission,
bulletinTextScrape: bulletinMetaText
};
console.log(newBulletin);
InsertOrUpdateBulletin(newBulletin, req.user)
.then(result => {
res.status(200).json({
data: result,
status: 200, //Created
statusMessage: "Success: Bulletin Created"
});
})
.catch(error => {
next({
status: 500, //Server Error
statusMessage: "Error: Failed to save Bulletin ",
catchError: error //System error
});
});
}
}
);
});
}
});
}
This might help (keyword: might) but it uses express-fileupload. Still allows for server side manipulation of the data.
https://link.medium.com/U1SdsoHMy2

express body-parser and multer value receiving issue?

I am giving post request /product/create with some value and an image.
if I console every value before
upload(req, res, (err) => {})
it is showing properly with out image info.
if I receive the value after upload(req, res, (err) => {})
No value is showing.
Full post request code:
app.post('/product/create', (req, res) => {
let filename;
upload(req, res, (err) => {
if(err){
res.render('index', {
msg: err
});
} else {
if(req.file == undefined){
res.render('index', {
msg: 'Error: No File Selected!'
});
} else {
res.render('index', {
msg: 'File Uploaded!',
filename = req.file.filename;
});
}
}
});
const product = {
title : req.body.title,
desc : req.body.desc,
image : filename,
}
});
configuring Multer:
const storage = multer.diskStorage({
destination: './public/uploads/',
filename: function(req, file, cb){
cb(null,file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
const upload = multer({
storage: storage,
limits:{fileSize: 1000000},
fileFilter: function(req, file, cb){
checkFileType(file, cb);
}
}).single('myImage');
function checkFileType(file, cb){
const filetypes = /jpeg|jpg|png|gif/;
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = filetypes.test(file.mimetype);
if(mimetype && extname){
return cb(null,true);
} else {
cb('Error: Images Only!');
}
}
Multer does not support 'req.file.filename' outside upload function. As filename, originalname, fieldname etc is inbuild API of multer. It is limited to upload function only.
Now, if you are trying to upload product values inside database then you have to create an insert function inside multer upload function only.

Can't get multer filefilter error handling to work

I'm playing around with file uploading in Node.js/Multer.
I got the the storage and limits working. But now im playing around with filefilter to simply deny some files by mimetype like this:
fileFilter: function (req, file, cb) {
if (file.mimetype !== 'image/png') {
return cb(null, false, new Error('goes wrong on the mimetype'));
}
cb(null, true);
}
When a file gets uploaded that is not a PNG it wont accept it.
But it also won't trigger the if(err)
When the file is to big it does generate the error. So somehow I need to generate an err on filefilter as well but im not sure how and guess new Error is wrong.
So, how am I supposed to generate an error if the file is not correct? What am I doing wrong?
Full code:
var maxSize = 1 * 1000 * 1000;
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, 'public/upload');
},
filename: function (req, file, callback) {
callback(null, file.originalname);
}
});
var upload = multer({
storage : storage,
limits: { fileSize: maxSize },
fileFilter: function (req, file, cb) {
if (file.mimetype !== 'image/png') {
return cb(null, false, new Error('I don\'t have a clue!'));
}
cb(null, true);
}
}).single('bestand');
router.post('/upload',function(req,res){
upload(req,res,function(err) {
if(err) {
return res.end("some error");
}
)}
)}
fileFilter function has access to request object (req). This object is also available in your router.
Therefore in fileFitler you can add property with validation error or validation error list (you can upload many files, and some of them could pass).
And in router you check if property with errors exists.
in filter:
fileFilter: function (req, file, cb) {
if (file.mimetype !== 'image/png') {
req.fileValidationError = 'goes wrong on the mimetype';
return cb(null, false, new Error('goes wrong on the mimetype'));
}
cb(null, true);
}
in router:
router.post('/upload',function(req,res){
upload(req,res,function(err) {
if(req.fileValidationError) {
return res.end(req.fileValidationError);
}
)}
)}
You can pass the error as the first parameter.
multer({
fileFilter: function (req, file, cb) {
if (path.extname(file.originalname) !== '.pdf') {
return cb(new Error('Only pdfs are allowed'))
}
cb(null, true)
}
})
Change the fileFilter and pass an error to the cb function:
function fileFilter(req, file, cb){
if(file.mimetype !== 'image/png'){
return cb(new Error('Something went wrong'), false);
}
cb(null, true);
};
fileFilter callback should be:
fileFilter: async (req, file, cb) => {
if (file.mimetype != 'image/png') {
cb(new Error('goes wrong on the mimetype!'), false);
}
cb(null, true);
}
Error handling in request:
access multer error using MulterError
const multer = require("multer");
router.post('/upload', function(req, res) {
upload(req, res, function(err) {
// FILE SIZE ERROR
if (err instanceof multer.MulterError) {
return res.end("Max file size 2MB allowed!");
}
// INVALID FILE TYPE, message will return from fileFilter callback
else if (err) {
return res.end(err.message);
}
// FILE NOT SELECTED
else if (!req.file) {
return res.end("File is required!");
}
// SUCCESS
else {
console.log("File uploaded successfully!");
console.log("File response", req.file);
}
)}
})

Resources