How do I make express middleware in class? - express

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",
});
}
}

Related

Using aws-sdk to upload to DigitalOceans

I'm using aws-sdk to upload images to DigitalOceans bucket. On localhost it works 100% but production seems like the function goes on without an error but the file does not upload to the bucket.
I cannot figure out what is going on and can't think of a way to debug this. tried aswell executing the POST request with Postman multipart/form-data + adding file to the body of the request and it is the same for localhost, working, and production is not.
my api endpoint:
import AWS from 'aws-sdk'
import formidable from "formidable"
import fs from 'fs'
const s3Client = new AWS.S3({
endpoint: process.env.DO_SPACES_URL,
region: 'fra1',
credentials: {
accessKeyId: process.env.DO_SPACES_KEY,
secretAccessKey: process.env.DO_SPACES_SECRET
}
})
export const config = {
api: {
bodyParser: false
}
}
export default async function uploadFile(req, res) {
const { method } = req
const form = formidable()
const now = new Date()
const fileGenericName = `${now.getTime()}`
const allowedFileTypes = ['jpg', 'jpeg', 'png', 'webp']
switch (method) {
case "POST":
try {
form.parse(req, async (err, fields, files) => {
const fileType = files.file?.originalFilename?.split('.').pop().toLowerCase()
if (!files.file) {
return res.status(400).json({
status: 400,
message: 'no files'
})
}
if (allowedFileTypes.indexOf(fileType) === -1) {
return res.status(400).json({
message: 'bad file type'
})
}
const fileName = `${fileGenericName}.${fileType}`
try {
s3Client.putObject({
Bucket: process.env.DO_SPACES_BUCKET,
Key: `${fileName}`,
Body: fs.createReadStream(files.file.filepath),
ACL: "public-read"
}, (err, data) => {
console.log(err)
console.log(data)
})
const url = `${process.env.FILE_URL}/${fileName}`
return res.status(200).json({ url })
} catch (error) {
console.log(error)
throw new Error('Error Occured While Uploading File')
}
});
return res.status(200)
} catch (error) {
console.log(error)
return res.status(500).end()
}
default:
return res.status(405).end('Method is not allowed')
}
}

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}`)} />

multer uploading multiple files

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

Express router with middleware error handling

I have a question about error handling with middleware, specifically multer. I have this route:
router.post('/', saveFile, (req, res, next) => {
//rest of code
})
Then I have saveFile middleware:
const multer = require('multer')
const storage = multer.diskStorage({
destination: (req, res, cb) => {
cb(null, './uploads/')
},
filename: (req, res, cb) => {
cb(null, new Date().getTime() + '.jpg')
}
})
const fileFilter = (req, file, cb) => {
if (file.mimetype === 'image/jpeg') cb(null, true)
cb(null, false)
}
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 3 // up to 3 megabytes
},
fileFilter: fileFilter
})
const saveFile = upload.single('file')
module.exports.saveFile = saveAudio
The issue I have is that when I upload a file with a field name other than file, I get an error MulterError: Unexpected field. I want to catch this error somehow. But I don't even know where to do that. How do I do that?
The answer was pretty simple, yet nobody answered.
in the app.js where express is setup, you can make a middleware for handling errors
app.use((error, req, res, next) => errorHandlers(error, req, res, next))
And put it at last.
and then ErrorHandlers.js:
module.exports = function(error, req, res, next) {
if (error.name === 'MulterError') {
// handle error here
} else {
next()
}
}

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?