uploading two files from two fields with multer as middleware - javascript

i'm trying to upload two files with different file extensions with multer from two fields but when i try it with postman the result always for the file is null, what is the solution for my problem? here is my code
middleware/uploadEpub
const multer = require('multer')
exports.uploadEpub = (epubFile, coverFile) => {
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "uploads")
},
filename: function (req, file, cb) {
cb(null, Date.now() + '-' + file.originalname.replace(/\s/g, ""))
}
})
const upload = multer({
storage
}).fields([{name: "bookFile", maxCount: 1},{name: "coverFile", maxCount: 1}])
}
controller/book
exports.addBook = async (req, res) => {
try {
const { ...data } = req.body
const newBook = await book.create({
...data,
bookFile: req.file,
coverFile: req.file
})
let bookData = await book.findOne({
where: {
id: newBook.id
},
attributes:{
exclude: ['createdAt','updatedAt']
}
})
bookData = JSON.parse(JSON.stringify(bookData))
res.send({
status: "Success",
Book: {
...bookData
}
})
} catch (error) {
console.log(error)
res.status(500).send({
status: "Failed",
message: "Server Error"
})
}
}

Multer set up
const multer = require('multer')
const path = require('path')
const { nanoid } = require('nanoid')
//Set Storage Engine
const storage = multer.diskStorage({
destination: './public/uploads/',
filename: (req, file, callback) => {
const id = nanoid(6)
const newFilename = `${file.fieldname}_${id}-${new Date().toISOString().replace(/:/g, '-')}${path.extname(file.originalname)}`
callback(null, newFilename)
}
})
const upload = multer({
storage: storage,
limits: { fileSize: 5572864 }, // up to 5.5 MB
fileFilter: (req, file, callback) => {
checkFileType(file, callback)
},
})
//Check File Type
const checkFileType = (file, cb) => {
//Allowed extensions
const fileType = /jpeg|jpg|png|gif|svg|pdf|epub/
//Check extension
const extname = fileType.test(path.extname(file.originalname).toLowerCase())
//Check mimetype
const mimetype = fileType.test(file.mimetype)
if (extname && mimetype) {
return cb(null, true)
} else {
return cb('Error: wrong file type!')
}
}
module.exports = upload
/***** middlewares.js *****/
module.exports.imageUploader = (req, res, next)=>{
const files = req.files
const uploadedFiles = []
for (var i = 0; i <images.length; i++){
uploadFiles.push('https://yourserver.com/public/uploads/' + files[i].filename)
}
req.uploadFiles = uploadFiles.toString() //appending files to req
next()
return
}
/**** index.js or app.js ****/ //where your routes are defined
router.post('/books/add', upload.array('images', 10), imageUploader, book.addBook) // allowing up to 10 files to be uploaded, calling imageUploader as a middleware
/*** Controllers/book ***/
exports.addBook = async (req, res) => {
const uploadedFiles = req.uploadFiles; //catching files from imageUploader middleware
// ... rest of your code
}

Try to upload the file as form-data in postman, and put the key with the same name that you have set in multer, the postman will look like this:

Try this:
const cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])
app.post('/cool-profile', cpUpload, function (req, res, next) {
})

Related

Uploading multiple files to multiple locations with Multer & Node.JS

I am trying to write an app using NodeJS and Express which will upload both a user avatar which is a single image (jpeg, png or webp) and 'legalDocs' which is essentially also a single file that can either be an image (jpeg, png or webp) or a pdf file. The avatar needs to be uploaded to ./resources/(userID)/userAvatar and the legal docs to ./resources/(userID)/legalDocs. Along with some other text info that will be posted along with the request as a multipart/form-data.
The userID is only generated after processing the text data posted.
Here's my current non-working code
APP.JS
//ENV
require("dotenv").config();
require("express-async-errors");
//EXPRESS
const express = require("express");
const server = express();
global.__basedir = __dirname;
//routes
const authRouter = require("./routes/authRoutes.js");
server.use(express.json());
server.use(express.urlencoded({extended: true}));
server.use(express.static("./public"));
server.use("/api/v0/auth", authRouter);
const port = process.env.PORT || 3000;
const start = async () => {
try {
server.listen(port, () => {
console.log(`--> Listening on port ${port}...`);
});
} catch (error) {
console.log(error);
console.log("Fatal error!");
}
}
start();
authRoutes.js:
const express = require("express");
const router = express.Router();
const {signupWithEmail} = require("../controllers/auth.js");
router.route("/signup").post(signupWithEmail);
module.exports = router;
../controllers/auth.js:
const multer = require("multer");
const processReq = require("../middleware/upload.js");
const signupWithEmail = async (req, res, next) => {
//Processing to get the ID happens here.
//For simplicity we can pretend like the ID is directly
//posted in a field with the name id.
const id = req.body.id;
console.log("id:");
console.log(id);
await processReq(id)(req, res, next);
res.status(200).send("done!");
}
upload.js
const multer = require("multer");
const errors = require("../errors");
const maxSize = 2 * 1024 * 1024;
const acceptableFileMIMETypes = ["image/jpeg", "image/png", "image/webp", "application/pdf"];
const userAvatarFilter = function (req, file, cb) {
if ((file.fieldname === "userAvatar" || file.fieldname === "legalDocs")
&& acceptableFileMIMETypes.includes(file.mimetype)
&& !(file.fieldname === "userAvatar" && file.mimetype === "application/pdf")) {
if (file.fieldname === "userAvatar") {
cb(null, true);
} else {
cb(null, false);
}
} else {
cb(new errors.BadRequestError("Both files are required, and must be JPEG, PNG, WEBP. Legal documents can also be PDF."));
}
}
const userlegalDocsFilter = function (req, file, cb) {
if ((file.fieldname === "userAvatar" || file.fieldname === "legalDocs")
&& acceptableFileMIMETypes.includes(file.mimetype)
&& !(file.fieldname === "userAvatar" && file.mimetype === "application/pdf")) {
if (file.fieldname === "userAvatar") {
cb(null, false);
} else {
cb(null, true);
}
} else {
cb(new errors.BadRequestError("Both files are required, and must be JPEG, PNG, WEBP. Legal documents can also be PDF."));
}
}
const uploadFunc = (id) => {
return (req, res, next) => {
const userAvatarStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, __basedir + `/resources/${id}/userAvatar`);
},
filename: (req, file, cb) => {
console.log("Uploaded avatar!");
console.log(file.originalname);
console.log(file);
cb(null, file.originalname);
}
});
const legalDocsStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, __basedir + `/resources/${id}/legalDocs`);
},
filename: (req, file, cb) => {
console.log("Uploaded legalDocs!");
console.log(file.originalname);
console.log(file);
cb(null, file.originalname);
}
});
const uploadAvatar = multer({
storage: userAvatarStorage,
limits: {
fileSize: maxSize,
files: 2
},
fileFilter: userAvatarFilter
}).any();
const uploadLegalDocs = multer({
storage: legalDocsStorage,
limits: {
fileSize: maxSize,
files: 2
},
fileFilter: userlegalDocsFilter
}).any();
uploadAvatar(req, res, next);
uploadLegalDocs(req, res, next);
next();
}
}
module.exports = uploadFunc;
The Problem
The first problem with my code is quite obvious, I cannot parse text data from the request unless it had been processed by multer (with the current code, console.logging the id shows that it's undefined), but I need the ID to be passed to multer in order to save the files to the correct directory. It seems like I need some way to process text only and generate the ID, then process the files.
The second problem is that, even if the ID is static - meaning that in ./controllers/auth.js the signupWithEmail function became:
const multer = require("multer");
const processReq = require("../middleware/upload.js");
const signupWithEmail = async (req, res, next) => {
const id = 1337;
console.log("id:");
console.log(id);
await processReq(id)(req, res, next);
res.status(200).send("done!");
}
I this is the output I get on the console when I try to post:
id:
1337
Uploaded legalDocs!
cours-analyse-numerique-ch1-21_22.pdf
{
fieldname: 'legalDocs',
originalname: 'cours-analyse-numerique-ch1-21_22.pdf',
encoding: '7bit',
mimetype: 'application/pdf'
}
node:_http_outgoing:771
throw new ERR_HTTP_HEADERS_SENT('remove');
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot remove headers after they are sent to the client
at new NodeError (node:internal/errors:400:5)
at ServerResponse.removeHeader (node:_http_outgoing:771:11)
at write (C:\Users\der_u\source\repos\ATEEK\node_modules\finalhandler\index.js:282:9)
at AsyncResource.runInAsyncScope (node:async_hooks:204:9)
at listener (C:\Users\der_u\source\repos\ATEEK\node_modules\on-finished\index.js:170:15)
at onFinish (C:\Users\der_u\source\repos\ATEEK\node_modules\on-finished\index.js:101:5)
at callback (C:\Users\der_u\source\repos\ATEEK\node_modules\ee-first\index.js:55:10)
at IncomingMessage.onevent (C:\Users\der_u\source\repos\ATEEK\node_modules\ee-first\index.js:93:5)
at IncomingMessage.emit (node:events:513:28)
at endReadableNT (node:internal/streams/readable:1359:12) {
code: 'ERR_HTTP_HEADERS_SENT'
}
and only 'legalDocs' get uploaded.
Is there any way to solve these problems without using a temporary directory to save all the files then move them once the ID had been generated? And what does the [ERR_HTTP_HEADERS_SENT] error mean?

file upload multier MulterError: Unexpected field

Not able to upload image in MongoDB database using NodeJS
Middleware fieldName and key name is also same still not working. no solution on internet
server.js
import cookieParser from "cookie-parser";
import multer from "multer";
export const app = express();
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: true}))
let upload = multer()
app.use(upload.array())
app.use("/",routLog,router)
app.listen(process.env.PORT,()=>{
db()
console.log(`Server is Runing on http://localhost:${process.env.PORT}`)
})
blogroute.js
import { fileUploder } from "../middelware/fileUploder.js";
export const blogRouter = Router()
blogRouter.post('/:user/create-post', fileUploder.single("file"), blogsController.createPos
This middleware file fileUplad.js which is use to store in upload folder and then from that will store in MongoDB in buffer ferment
import { randomBytes } from "crypto";
import multer from "multer";
import path from "path";
const storage = multer.diskStorage({
destination: './upload',
filename: async (req, file, callback) => {
return await randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString("hex") + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: "upload"
};
resolve(fileInfo);
})
}
})
export const fileUploder = multer({
storage: storage,
limits: { fileSize: 2000 },
fileFilter: (req, file, callback) => {
allowedFileTypes = /jpeg|jpg|png|gif/;
if (file.mimetype == 'jpg' || file.mimetype == 'jpeg' || file.mimetype == 'png', file.mimetype == 'gif') {
return cb(null, true);
} else {
cb("Invalid file type. Only JPEG, PNG and GIF file are allowed.")
}
}
})
MulterError: Unexpected field
at wrappedFileFilter (/mnt/ssd/BlogProject/backendapi/node_modules/multer/index.js:40:19)
at Multipart.<anonymous> (/mnt/ssd/BlogProject/backendapi/node_modules/multer/lib/make-middleware.js:107:7)
at Multipart.emit (node:events:527:28)
at HeaderParser.cb (/mnt/ssd/BlogProject/backendapi/node_modules/busboy/lib/types/multipart.js:358:14)
at HeaderParser.push (/mnt/ssd/BlogProject/backendapi/node_modules/busboy/lib/types/multipart.js:162:20)
at SBMH.ssCb [as _cb] (/mnt/ssd/BlogProject/backendapi/node_modules/busboy/lib/types/multipart.js:394:37)
at feed (/mnt/ssd/BlogProject/backendapi/node_modules/streamsearch/lib/sbmh.js:248:10)
at SBMH.push (/mnt/ssd/BlogProject/backendapi/node_modules/streamsearch/lib/sbmh.js:104:16)
at Multipart._write (/mnt/ssd/BlogProject/backendapi/node_modules/busboy/lib/types/multipart.js:567:19)
at writeOrBuffer (node:internal/streams/writable:389:12)```
This is simple example :
let multer = require('multer');
let multerImage = multer().single('file');
function uploadAvatar(req, res) {
let phone = data.phoneNumber;
multerImage(req, res, () => {
let file = req.file;
if (isUndefined(file))
return Json.builder(Response.HTTP_BAD_REQUEST);
let {
fileUrl
} = File.validationAndWriteFile(file.buffer, Util.getFileFormat(file.originalname));
Update.img(phone, fileUrl, result => {
if (!result)
return Json.builder(Response.HTTP_BAD_REQUEST);
Json.builder(Response.HTTP_OK);
});
});
}

Get image path with Multer Express Nodejs

I am using Multer to save images but I need to get the path of the image to save it to MongoDB. I am trying to get the path with req.file but it always tells me on the console that it is undefined.
this is my route:
import { Router } from 'express';
import { check, validationResult } from 'express-validator';
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/products')
},
filename: function (req, file, cb) {
cb(null, new Date().toISOString().replace(/:/g, '-') + file.originalname)
}
});
const fileFilter = (req, file, cb) => {
// reject a file
if (file.mimetype === 'image/jpeg' ||file.mimetype === 'image/png') {
cb(null, true);
} else {
cb(null, false);
//cb(new Error('I don\'t have a clue!'))
}
}
const upload = multer(
{ storage: storage,
limits:{
fileSize: 1024 * 1024
},
fileFilter: fileFilter
});
let router = Router();
router.post('/', upload.single('img'),
newProduct
);
And in the new Product controller I am trying to read the req.file but the console tells me that it is undefined:
Controller:
import { Product } from '../models'
let newProduct = async (req, res = response ) => {
console.log('file ' + req.file); //UNDEFINED
try {
let { status, user, ...body } = req.body;
let productDB = await Product.findOne ( { 'name': body.name } );
if (productDB) {
return res.status(400).json({
msg:`El producto ${ productDB.name } ya existe`
})
}
let data = {
...body,
name: body.name,
user: req.user._id
}
let product = new Product( data );
await product.save();
res.status(200).json( product );
} catch (error) {
return res.status(400).json({
error
});
}
}
Console:
Thanks for your help.
you can try to do this in filename instead:
filename: function (req, file, cb) {
req.imageName = new Date().toISOString().replace(/:/g, '-') + file.originalname
cb(null, req.imageName)
}
then there:
console.log('file ' + req.file); //UNDEFINED
//you can get imageName instead
console.log('imageName',req.imageName)
//if you want url to store in database you can do this
//supposing your have images directory in root of your node server
const url = `${req.protocol}://${req.get('host')}/images/${req.body.image}`

Upload multiple files from dynamically created form inputs

I'm learning MEAN stack and have trubles on a files upload. I have a company form:
this.companyForm = this.fb.group({
trucks: this.fb.array([]),
...
});
Trucks field is dynamicly created:
newTruck(): FormGroup {
this.added = false;
return this.fb.group({
...
TLic: new FormControl(null, {
validators: [Validators.required]
}),
Lic: new FormControl(null, {
validators: [Validators.required]
}),
CMRLic: new FormControl(null, {
validators: [Validators.required]
})
})
}
addTruck() {
this.trucks().push(this.newTruck());
}
On save form:
this.authService.createUserStep1(
...
this.companyForm.value.trucks
);
AuthService:
createUserStep1(... trucks: Array<any>) {
const AuthDataStep1: AuthDataStep1 = {
...
trucks: trucks
};
this.http.put(BACKEND_URL + "signupStep1/", AuthDataStep1).subscribe(
() => {
this.authStatusListener.next(true);
this.router.navigate(["auth/signupStep2"]);
},
error => {
this.authStatusListener.next(false);
}
);
}
Nodejs controller where I write to DB:
exports.createUserStep1 = (req, res, next) => {
...
for (i = 0; i < req.body.trucks.length; i++) {
const truck = new Truck({
...
TLic: req.body.trucks[i].file.TLic[0].filename,
Lic: req.body.trucks[i].file.Lic[0].filename,
CMRLic: req.body.trucks[i].file.CMRLic[0].filename,
});
Truck.create(truck);
}
})
}
Middleware for files upload:
const multer = require("multer");
var path = require('path');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "backend/truckdocs");
},
filename: (req, file, cb) => {
const name = file.originalname
.toLowerCase()
.split(" ")
.join("-");
cb(null, name + "-" + Date.now() + "." + path.extname(file.originalname));
}
});
module.exports = multer({ storage: storage }).fields([
{
name: "TLic", maxCount: 1
}, {
name: "Lic", maxCount: 1,
}, {
name: "CMRLic", maxCount: 1
}
]);
and route
const express = require("express");
const UserController = require("../controllers/user");
const extractTruckFiles = require("../middleware/truckfiles");
const checkAuth = require("../middleware/check-auth");
const router = express.Router();
/*App*/
...
router.put("/signupStep1", checkAuth, extractTruckFiles, UserController.createUserStep1);
I want to create x number of trucks in form and for each truck upload 3 files(TLic, Lic, CMRLic).What is the best way to achieve this? I know it needs time for you to get into my code and I'm very thankful in advance.
This is how i did for a ecommerce where i upload 3 photo. I have done it with MVC pattern and i called multer in route file.
const express = require("express")
const multer = require("multer");
const router = express.Router()
const productController = require("../../controller/vendor/product")
const app = express();
const fileStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "images");
},
filename: (req, file, cb) => {
cb(null, new Date().toISOString().replace(/:/g, '-') + '-' +file.originalname)
}
});
const fileFilter = (req, file, cb) => {
if(file.mimetype === 'image/png' || file.mimetype === 'image/jpg' || file.mimetype === 'image/jpeg') {
cb(null, true)
} else {
cb(null, false)
}
}
let filehandler = app.use(multer({storage : fileStorage, fileFilter: fileFilter }).array('image',3))
/* #GET product Page request */
router.get('/products', productController.getProduct)
/* #GET add product page Request */
router.get('/add-product', productController.getAddProduct)
/* #POST Request */
router.post('/add-product', filehandler, productController.postAddProduct)
/* #GET AJAX Request findind subcategory */
router.get("/getSubCategory/:id", productController.getSubCategory);
module.exports = router

How to set field name to upload file single/array Multer NodeJs

I have a code for upload images in nodejs using multer and sharp. But i want to ask how to set dynamic array name at line 19? I want to set that array name from the nodejs routes, because my field name in mongodb collections is different in every collection.
This is my middleware upload code:
const multer = require("multer");
const sharp = require("sharp");
const multerStorage = multer.memoryStorage();
const multerFilter = (req, file, cb) => {
if (file.mimetype.startsWith("image")) {
cb(null, true);
} else {
cb("Please upload only images.", false);
}
};
const upload = multer({
storage: multerStorage,
fileFilter: multerFilter
});
const uploadFiles = upload.array("images", 10); //Change this 'images' name from route
const uploadImages = (req, res, next) => {
uploadFiles(req, res, err => {
if (err instanceof multer.MulterError) {
if (err.code === "LIMIT_UNEXPECTED_FILE") {
return res.send("Too many files to upload.");
}
} else if (err) {
return res.send(err);
}
next();
});
};
const resizeImages = async (req, res, next) => {
if (!req.files) return next();
req.body.images = [];
await Promise.all(
req.files.map(async file => {
const filename = file.originalname.replace(/\..+$/, "");
const newFilename = `cariboss-${filename}-${Date.now()}.jpeg`;
await sharp(file.buffer)
.resize(640, 320)
.toFormat("jpeg")
.jpeg({ quality: 90 })
.toFile(`uploads/${newFilename}`);
req.body.images.push(newFilename);
})
);
next();
};
const getResult = async (req, res) => {
if (req.body.images.length <= 0) {
return res.send(`You must select at least 1 image.`);
}
const images = req.body.images
.map(image => "" + image + "")
.join("");
return res.send(`Images were uploaded:${images}`);
};
module.exports = {
uploadImages: uploadImages,
resizeImages: resizeImages,
getResult: getResult
};
My routes code:
module.exports = app => {
const articles = require("../controllers/articles.controller.js");
const upload = require("../middlewares/upload");
var router = require("express").Router();
router.post("/", upload.uploadImages, upload.resizeImages, upload.getResult, articles.create);
app.use("/api/article", verifyToken, router);
};
I have already search the solution on internet, but there is nothing. Thanks for your help sir.

Categories