multer uploading multiple files - express

So, I have two properties in my schema. 1) ImageCover (single file) and 2) Images (array of images)
I am using multer's upload.fields method to upload both of these files. when I upload both, it works.But when I try to upload only either of them I am getting error that the other field is not defined. How can I fix this?
1) multer.js
module.exports = () => {
let multer = require('multer')
let myStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './uploads/')
},
filename: (req, file, cb) => {
cb(null, `${Date.now()}-${file.originalname}`)
}
})
filter = (req, file, cb) => {
let mimeType = file.mimetype.split('/')[0]
if (mimeType !== 'image') {
req.fileError = true
cb(null, false)
} else {
cb(null, true)
}
}
let upload = multer({
storage: myStorage,
fileFilter: filter
})
return upload
}
2) uploading photo file
module.exports = function Check(req) {
if (req.files.images) {
req.body.images = []
let allFiles = req.files.images
allFiles.forEach(file => {
let mimeType = file.mimetype.split('/')[0]
if (mimeType !== 'image') {
fs.unlink(path.join(process.cwd(), 'uploads/'), (err, done) => {
if (err) console.log(err)
})
}
req.body.images.push(file.filename)
})
}
if (req.files.imageCover[0]) {
let file = req.files.imageCover[0]
let mimeType = file.mimetype.split('/')[0]
if (mimeType !== 'image') {
fs.unlink(path.join(process.cwd(), 'uploads/'), (err, done) => {
if (err) console.log(err)
})
}
req.body.imageCover = file.filename
}
}
3) create controller (check is the function that I have exported above)
exports.createTour = (req, res, next) => {
check(req)
if (req.fileError) { return next({ error: 'invalid file format dude' }) }
Tour.create({
...req.body,
owner: req.user._id
}).then(result => {
res.status(201).json({
status: 'success',
total: result.length,
result
})
}).catch(err => next(err))
}
4) Route handler
Router.route('/')
.post(authController.protect,
upload.fields([
{
name: 'imageCover', maxCount: 1,
},
{
name: 'images', maxCount: 10
}
]),
tourController.createTour)
response in postman when I only select images

I think you get this error because you are trying to access first element of array, and array is empty or not defined.
For example:
if (req.files.imageCover[0])
try to refactor to look like this:
if (req.files.hasOwnProperty("imageCover") && req.files.imageCover.length > 0)
you should do validations like this on all places where objects/arrays can be optional

Related

How do I make express middleware in class?

I currently use multer middleware like below
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "public");
},
filename: function (req, file, cb) {
cb(null, req.params.id + "_" + file.originalname);
},
});
export const multerUploadSingle = (req: Request, res: Response, next: NextFunction) => {
const upload = multer({ storage: storage }).single("file");
upload(req, res, (error: unknown) => {
if (error instanceof multer.MulterError) {
const message = `file upload fail: ${error.message}`;
next(new HttpException(message, HttpStatus.BadRequest));
} else if (error instanceof Error) {
const message = `file upload fail: ${error.message}`;
next(new HttpException(message, HttpStatus.InternalServerError));
} else {
// upload success
next();
}
});
}
and use in router like this
FileRouter.post("/upload/:id", multerUploadSingle, (req, res) => {...});
However, I felt I want to refactor this middleware in class, and rewrote the code like this,
export class Multer {
private readonly storage: multer.StorageEngine;
constructor() {
this.storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "public");
},
filename: function (req, file, cb) {
cb(null, req.params.id + "_" + file.originalname);
},
});
}
uploadSingle(req: Request, res: Response, next: NextFunction) {
const upload = multer({ storage: this.storage }).single("file");
upload(req, res, (error: unknown) => {
if (error instanceof multer.MulterError) {
const message = `file upload fail: ${error.message}`;
next(new HttpException(message, HttpStatus.BadRequest));
} else if (error instanceof Error) {
const message = `file upload fail: ${error.message}`;
next(new HttpException(message, HttpStatus.InternalServerError));
} else {
// upload success
next();
}
});
}
}
const multer = new Multer();
FileRouter.post("/upload/:id", multer.uploadSingle, (req, res) => {...});
With my short knowledge, I think both case should have the same result, but the latter case which uses class made middleware doesn't work at all. It's seems method "uploadSingle" is never called, thus multer not uploading the file.
Did I make any mistake with class usage? or is it just express can only use function defined middleware?
Your code should follow the MVC pattern.
You can do stuff like this:
routerFile.js
const upload = require("../../configs/multer");
const postController = require("../../controllers/postController");
const multiUploadEvent = upload.fields([
{ name: "images", maxCount: 2 },
{ name: "video", maxCount: 2 }
]);
router.post("/add-event-post", multiUploadEvent, postController.addEventPost);
module.exports = router;
multer.js
const multer = require('multer');
const multerFilter = (req, file, cb) => {
console.log("Mime type :", file.mimetype.split('/')[0]);
if (file.mimetype.split('/')[0] === 'image' || file.mimetype.split('/')[0] === 'video' || file.mimetype.split('/')[0] === 'audio') {
cb(null, true);
} else {
cb(new Error('Please upload img, audio, or video file only.'), false);
}
};
const storage = multer.memoryStorage();
const upload = multer({
storage: storage,
fileFilter: multerFilter,
limits: {
fileSize: , 50 * 1024 * 1024// 50 Mb
},
});
module.exports = upload;
postController.js
const addEventPost = async (request, response) => {
try {
let { title, ..... } = request.body;
const images = request.files.images;
const video = request.files.video;
console.log(title);
console.log(images);
console.log(videos);
//upload to services likes aws and save to database
.
.
.
return response
.status(200)
.json({
message: "Event post added successfully"
});
} catch (error) {
console.log(error);
response.status(500).json({
error: "Something went wrong",
});
}
}

How to upload and get image / formData with react native axios and multer

I try to post images to MongoDB but get status CODE 404,
I have multer and static path '/uploads/' and this directory is on the frontend with some images, I get the array from the server but i dont know how to show them :
i tried in flat list to show them like:
<Image source={require("../../server/uploads/a3.jpg")} />//here its work.
and when i replace to {require("../../server/${item.imageurl}" iI get error
when i console log the require path I get the objects with url->> ../../server/uploads\a1.jpg
see that the / is the opposite side \ , maybe its the problem?
now for upload I tried to :
I getting object(selectedImage) when I choosing file :
"localUri": "file:/data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FLior-
3698f362-fc81-4a77-8c97-8cd1349ce090/ImagePicker/d4c4b4ec-72e6-4e20-b6eb-4727bc84d93e.jpg",
and my axios post:
if (selectedImage !== null) {
let formData = new FormData();
formData.append("image", selectedImage);
console.log(formData);
try {
const response = await indexApi.post(`/uploads`, formData);
console.log("res", response);
} catch (err) {
console.log("c", err);
}
}
};
the form data that i send look like:
FormData {
"_parts": Array [
Array [
"image",
Object {
"localUri":
"file:/data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FLior-3698f362-fc81-
4a77-8c97-8cd1349ce090/ImagePicker/d4c4b4ec-72e6-4e20-b6eb-4727bc84d93e.jpg",
},
],
],
}
and I get error 404, or 503.....
the backend parts:
image controller:
const Image = require("../models/image");
const _ = require("lodash");
exports.getImages = (req, res) => {
Image.find()
.select("_id image desc")
.then(images => {
res.json({ images });
})
.catch(err => console.log("get images errors", err));
};
//INSERT NEW IMAGE//
exports.uploadImage = (req, res) => {
const image = new Image({
image: req.file.path,
desc: req.body.desc,
});
console.log(image);
console.log(image);
console.log("ss", image);
image.save(err => {
if (err) {
return res.status(400).json({ error: "העלאת תמונה נכשלה" });
}
res.json({ message: "העלאת תמונה עברה בהצלחה" });
});
};
image route:
const express = require("express");
const { getImages, uploadImage } = require("../controllers/image");
const multer = require("multer");
//configure the images
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./uploads");
},
filename: function (req, file, cb) {
console.log("f", file);
cb(null, file.originalname);
},
});
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 55,
},
});
const router = express.Router();
router.get("/images", getImages);
router.post("/images/new", upload.single("image"), uploadImage);
module.exports = router;
so maybe my prolbem go from the route... beacuse i dont understand the that thing that i have one route (uploads) for the images, and another route with get and post method(/images) and for post (/images/new)
Edit. Error Picture for the first part
Try fixing this first:-
a. Your require might not able to read the incoming value from item.imageUrl since you called it inside double quotes (""):-
try using backticks (``):-
<Image source={require(`../../server/${item.imageUrl}`)} />
OR
You could try one of these two ways:-
saving image path in db
in imageUpload method/function:-
//INSERT NEW IMAGE//
exports.uploadImage = (req, res) => {
const image = new Image({
// don't do this
// image: req.file.path,
// instead try do this
image: '/uploads/' + req.file.originalname,
desc: req.body.desc,
});
console.log(image);
console.log(image);
console.log("ss", image);
image.save(err => {
if (err) {
return res.status(400).json({ error: "העלאת תמונה נכשלה" });
}
res.json({ message: "העלאת תמונה עברה בהצלחה" });
});
saving image originalname only in db
in imageUpload method/function:-
//INSERT NEW IMAGE//
exports.uploadImage = (req, res) => {
const image = new Image({
// don't do this
// image: req.file.path,
// instead try do this
image: req.file.originalname,
desc: req.body.desc,
});
console.log(image);
console.log(image);
console.log("ss", image);
image.save(err => {
if (err) {
return res.status(400).json({ error: "העלאת תמונה נכשלה" });
}
res.json({ message: "העלאת תמונה עברה בהצלחה" });
});
in 'view' or front-end:-
<Image source={require(`../../server/uploads/${item.imageUrl}`)} />

Request body is empty when submitting data using "form data"

When I update using raw JSON, it's working but when I use the form data it is not updating. the request body when using form data is an empty object. Why is this happening?
Here's my update code:
exports.updateProgram = catchAsync(async (req, res, next) => {
console.log('req ko body',req.body)
let doc = await Program.findByIdAndUpdate(req.params.id, req.body, { runValidators: true, new: true })
if (!doc) {
return next(new AppError('No document found with that ID', 404))
}
res.status(200).json({
status: 'success!',
data: { doc }
})
})
In Postman:
I am using multer, I actually pass the photo in req.body. Here's the code:
let multerStorage = multer.memoryStorage()
let multerFilter = (req, file, cb) => {
if (file.mimetype.split('/')[0] == 'image') {
cb(null, true)
} else {
cb(new AppError('Not an image!', 400), false)
}
}
let upload = multer({
storage: multerStorage,
fileFilter: multerFilter
})
exports.uploadPhotos = upload.fields([
{ name: 'abcd', maxCount: 10 },
{ name: 'photos', maxCount: 10 },
{name: 'photos3', maxCount: 10}
])
exports.resizePhotos = catchAsync(async (req, res, next) => {
// if (!req.files.photos || !req.files.abcd) return next()
if(req.files.abcd) {
req.body.abcd = []
await Promise.all(req.files.abcd.map(async (file, i) => {
let filename = `tour-${Date.now()}-${i + 1}.jpeg`
await sharp(file.buffer)
.resize(500,500)
.toFormat('jpeg')
.jpeg({ quality: 90 })
.toFile(`public/img/arpit/${filename}`)
req.body.abcd.push(filename)
})
)} else if(req.files.photos3) {
req.body.photos3 = []
await Promise.all(req.files.photos3.map(async (file, i) => {
let filename = `tour-${Date.now()}-${i + 1}.jpeg`
await sharp(file.buffer)
.resize(500,500)
.toFormat('jpeg')
.jpeg({ quality: 90 })
.toFile(`public/img/arpit/${filename}`)
req.body.photos3.push(filename)
})
)}
else if(req.files.photos) {
// console.log('codee here')
// } else if(req.body.photos) {
req.body.photos = []
console.log('req.files>>>', req.files)
await Promise.all(req.files.photos.map(async (file, i) => {
let filename = `tour-${Date.now()}-${i + 1}.jpeg`
await sharp(file.buffer)
.resize(500,500)
.toFormat('jpeg')
.jpeg({ quality: 90 })
.toFile(`public/img/programs/${filename}`)
req.body.photos.push(filename)
})
)
}
return next()
})
I'm importing in the routes file
Express (bodyParser) can't handle multipart form-data and that's why your code isn't working.
Take a look at multer, an express package. It is a middleware which provides the functionality you're looking for.
var cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }]);
app.post('/cool-profile', cpUpload, function (req, res, next) {
// req.files is an object (String -> Array) where fieldname is the key, and the value is array of files
//
// e.g.
// req.files['avatar'][0] -> File
// req.files['gallery'] -> Array
//
// req.body will contain the text fields, if there were any
})
This might be help you. Quoted from https://www.npmjs.com/package/multer#readme

Add and Retrieve Audio

I have added and retrived image in MongoDB using Node. Can I use the same code with some adjustment? Suggest me.
upload.ts
var multer = require("multer");
export let UPLOAD_PATH = "uploads";
const storage = multer.diskStorage({
destination: function(req, file, cb) {
req;
file;
cb(null, UPLOAD_PATH);
},
filename: function(req, file, cb) {
req;
cb(null, file.fieldname + "-" + Date.now() + ".jpg");
}
});
export const upload = multer({ storage: storage }).single("avatar");
image.controller.ts
Upload
this._model.findOne(
{ ["user"]: new mongoose.Types.ObjectId(user._id) },
img => {
upload(req, res, err => {
if (err) {
res.status(500).json(null);
} else {
// Create a new image model and fill the properties
let newImage = new Image();
newImage.filename = req.file.filename;
newImage.originalName = req.file.originalname;
newImage.desc = req.body.desc;
newImage.url =
req.protocol + "://" + req.get("host") + "/images/" + newImage._id;
newImage.user = user._id;
newImage.save(err => {
if (err) {
res.status(400).json(null);
} else {
res.status(201).json(img);
}
});
}
});
}
);
Retrive
getImage = (req, res) => {
const user = this.getUser(req, res);
this._model.findOne({ ['user']: new mongoose.Types.ObjectId(user._id) }, (err, image) => {
if (err) {
res.status(500).json(null);
}
else if (image == null) {
res.status(200).json(image);
} else {
// stream the image back by loading the file
res.setHeader('Content-Type', 'image/jpeg');
fs.createReadStream(path.join(UPLOAD_PATH, image.filename)).pipe(res);
}
})
};
Is it is possible to use same code with some modification to add and retrieve audio files using Node, Express in Mongo?

Image upload using react-admin

I am new to react-admin. I am using react-admin to upload the file. I have following the step mentioned below in tutorial.
But after I submit the request...I see http trace as follow. I see blob link instead of Base64 image payload.
{
"pictures": {
"rawFile": {
"preview": "blob:http://127.0.0.1:3000/fedcd180-cdc4-44df-b8c9-5c7196788dc6"
},
"src": "blob:http://127.0.0.1:3000/fedcd180-cdc4-44df-b8c9-5c7196788dc6",
"title": "Android_robot.png"
}
}
Can someone please advice how to get base64 image payload instead of link?
Check to see if you have this handler, most likely you did not change the name of the resource posts to your:
const addUploadCapabilities = requestHandler => (type, resource, params) => {
if (type === 'UPDATE' && resource === 'posts') {
Create your custom dataProvider to convert picture to base64
import restServerProvider from 'ra-data-json-server';
const servicesHost = 'http://localhost:8080/api';
const dataProvider = restServerProvider(servicesHost);
const myDataProfider = {
...dataProvider,
create: (resource, params) => {
if (resource !== 'your-route' || !params.data.pictures) {
// fallback to the default implementation
return dataProvider.create(resource, params);
}
const myFile = params.data.pictures;
if ( !myFile.rawFile instanceof File ){
return Promise.reject('Error: Not a file...'); // Didn't test this...
}
return Promise.resolve( convertFileToBase64(myFile) )
.then( (picture64) => ({
src: picture64,
title: `${myFile.title}`
}))
.then( transformedMyFile => dataProvider.create(resource, {
...params,
data: {
...params.data,
myFile: transformedMyFile
}
}));
}
};
const convertFileToBase64 = file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file.rawFile);
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
});
export default myDataProfider;
And get image data at your Server API
exports.create = (req, res) => {
if(req.body.myFile){
var file = req.body.myFile;
var fs = require('fs');
var data = file.src.replace(/^data:image\/\w+;base64,/, "");
var buf = Buffer.from(data, 'base64');
fs.writeFile(`upload/${file.title}`, buf, err => {
if (err) throw err;
console.log('Saved!');
});
}};