trying to upload an image using multer and getting this error
ENOENT: no such file or directory, open 'C:\Users\Marwa\mern-stack\uploads\images\74.jpeg'
POST /api/users/signup 500 23.065 ms - 1711
RangeError [ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code: ENOENT
at ServerResponse.writeHead (_http_server.js:248:11)
any ideas why this error happened?
this is the file-upload middleware
and the image should be stored in uploads/images folder
const multer = require("multer");
const MIME_TYPE_MAP = {
"image/png": "png",
"image/jpeg": "jpeg",
"image/jpg": "jpg",
};
const fileUpload = multer({
limits: { fileSize: 2 * 1024 * 1024 },
storage: multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "uploads/images");
},
filename: (req, file, cb) => {
const ext = MIME_TYPE_MAP[file.mimetype];
cb(null, Math.floor(Math.random() * 100) + "." + ext);
},
}),
fileFilter: (req, file, cb) => {
const isValid = !!MIME_TYPE_MAP[file.mimetype];
let error = isValid ? null : new Error("Invalid mime type!");
cb(error, isValid);
},
});
module.exports = fileUpload;
then I pass this middleware to route I want to add image for (signup)
router.post(
"/signup",
fileUpload.single("image"),
[
check("name").not().isEmpty(),
check("email").isEmail(),
check("password").isLength({ min: 6 }),
],
createUser
);
Related
After uploading an image to S3 bucket, it looks strange most of the time, but the same images looks OK sometimes, so I'm not sure what's going on. It started happening from last week and before that it was working fine.
Here is a sample image that I uploaded, and after uploading, it appears as follows.
https://hub-uploads-stage.s3.us-east-2.amazonaws.com/gallery-documents/file-1675056885128-alphabet-gcf3617eaf_1920.jpg
Original Image
https://i.imgur.com/ChbHNn9.png
I'm using packages: multer, multer-s3 with node js 16.19.0
I'm using following dependencies
multer: ^1.4.2
multer-s3: ^2.10.0
aws-sdk: ^2.1002.0
file-type: ^16.5.3
Here is the code I used to upload files.
import AWS from "aws-sdk";
import multer from "multer";
import multerS3 from "multer-s3";
import { fromBuffer } from 'file-type';
import stream from 'stream';
AWS.config.update({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
region: process.env.AWS_REGION,
signatureVersion: "s3v4",
});
const s3 = new AWS.S3();
export const basePath = {
profile: "profile-images",
post: "post-documents",
slider: "slider-images",
gallery: "gallery-documents",
icons: "icons",
posters: "product-posters",
chat: "chat"
}
const buckets = {
uploads: process.env.S3_BUCKET_UPLOADS || 'hub-uploads-stage'
}
const mimeTypes = {
image: [
"image/gif",
"image/jpg",
"image/jpeg",
"image/png",
"image/tiff",
"image/pipeg",
"image/svg+xml",
"image/bmp",
"image/x-xbitmap",
"image/x-icon"
]
}
const storage = (Bucket: any, uploadBasePath: any) => multerS3({
s3,
bucket: Bucket,
acl: 'public-read',
contentType: function (req: any, file: any, callback: any) {
file.stream.once('data', async function (firstChunk: any) {
var type = await fromBuffer(firstChunk)
var mime = (type === null ? 'application/octet-stream' : type?.mime)
var outStream = new stream.PassThrough()
outStream.write(firstChunk)
file.stream.pipe(outStream)
callback(null, mime, outStream)
})
},
metadata: function (req: any, file: any, callback: any) {
callback(null, { fieldName: file.originalname })
},
key: function (req: any, file: any, callback: any) {
let fileName = `${file.fieldname}-${Date.now()}-${file.originalname}`;
let filePath = uploadBasePath + '/' + fileName;
callback(null, filePath)
}
})
export const uploader = {
profile: multer({
storage: storage(buckets.uploads, basePath.profile),
fileFilter: function (req, file, callback) {
if (mimeTypes.image.includes(file.mimetype)) {
callback(null, true)
} else {
callback(null, false)
}
}
}),
post: multer({
storage: storage(buckets.uploads, basePath.post)
}),
slide: multer({
storage: storage(buckets.uploads, basePath.slider),
fileFilter: function (req, file, callback) {
if (mimeTypes.image.includes(file.mimetype)) {
callback(null, true)
} else {
callback(null, false)
}
}
}),
gallery: multer({
storage: storage(buckets.uploads, basePath.gallery),
fileFilter: function (req, file, callback) {
console.log("File in filter =>", file)
callback(null, true)
}
}),
icon: multer({
storage: storage(buckets.uploads, basePath.icons)
}),
productPoster: multer({
storage: storage(buckets.uploads, basePath.posters),
fileFilter: function (req, file, callback) {
if (mimeTypes.image.includes(file.mimetype)) {
callback(null, true)
} else {
callback(null, false)
}
}
}),
chat: multer({
storage: storage(buckets.uploads, basePath.chat)
})
};
Is there something i need to upgrade or change?
Thanks
I have try to insert some data into mongodb database using node js REST API but I got an error Unexpected field Im new to node please help me. whitePaper is my pdf file If I upload data like title, description and image only it gives the Correct answer with status code 201 but I try to upload all data and pdf but it gives the error
model code:
description: {
type: String,
required: true
},
imgURL: {
type: String,
required: true
},
whitePaperLink: {
type: String,
required: true,
},
app.js file
app.use('/whitePaper', express.static('whitePaper'));
router file
const whitePaperLink = multer.diskStorage({
destination: './whitePaper/',
filename: (req, file, cb) => {
return cb(null, `${file.fieldname}_${Date.now()}${path.extname(file.originalname)}`);
}
});
const whitePaperFilter = (req, file, cb) => {
if (file.mimetype === 'application/pdf') {
cb(null, true)
} else {
cb(null, false)
}
};
const whitePaperUpload = multer({
storage: whitePaperLink,
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: whitePaperFilter
});
router.post('/', checkAuth, imageUpload.single('imgURL'), whitePaperUpload.single('whitePaperLink'),
PostController.create_Post)
controller file
exports.create_Post = async (req, res) => {
const post = new Post({
title: req.body.title,
category: req.body.category,
description: req.body.description,
imgURL: req.file.path,
whitePaperLink: req.file.path,
publishDate: req.body.publishDate,
});
try {
const addPost = await post.save()
res.status(201).json({
message: 'Post Added Succesfully.'
})
} catch (error) {
console.log(error);
res.status(500).json({
message: error
})
}
}
If you'll use upload.single for each field it'll give error Unexpected Field.
Multer takes all files at once for execution, and in your case you've 2 different files and it'll take both files to upload.single.
So, instead of upload.single use upload.fields.
In your route.js, do it like this:
const destination = (req, file, cb) => {
switch (file.mimetype) {
case 'image/jpeg':
cb(null, './images/');
break;
case 'image/png':
cb(null, './images/');
break;
case 'application/pdf':
cb(null, './whitePaper/');
break;
default:
cb('invalid file');
break;
}
}
const storage = multer.diskStorage({
destination: destination,
filename: (req, file, cb) => {
return cb(null, `${file.fieldname}_${Date.now()}${path.extname(file.originalname)}`);
}
});
const fileFilter = (req, file, cb) => {
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png' || file.mimetype === 'application/pdf') {
cb(null, true)
} else {
cb(null, false)
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5,
},
fileFilter: fileFilter
});
// add post
router.post('/', upload.fields([{ name: 'imgURL', maxCount: 1 }, { name: 'whitePaperLink', maxCount: 1 }]), PostController.create_Post)
Edit:
You can also do it like this:
const uploadPostData = (req, res, next) => {
upload.fields([{ name: 'imgURL', maxCount: 1 }, { name: 'whitePaperLink', maxCount: 1 }])(req, res, (err) => {
console.log(req.files);
req.body.imgURL = req.files.imgURL[0].path.replace('/\\/g','/');
req.body.whitePaperLink = req.files.whitePaperLink[0].path.replace('/\\/g','/');
next()
})
}
// add post
router.post('/', uploadPostData, PostController.create_Post)
I am trying to upload images in mongo db but after clicking on send in postman it shows "img": null
I am using flutter for frontend so I want to make a rest api I am able to upload single image but when I am trying to upload multiple images it shows
"img": null
I have also created a schema
where I have set
img:{
type: array,
default:"",
}
const express = require("express");
const router = express.Router();
const Profile = require("../models/profile.model");
const middleware = require("../middleware");
const multer = require("multer");
const path = require("path");
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "./uploads");
},
filename: (req, file, cb) => {
cb(null, req.decoded.username + ".jpg");
},
});
const fileFilter = (req, file, cb) => {
if (file.mimetype == "image/jpeg" || file.mimetype == "image/png") {
cb(null, true);
} else {
cb(null, false);
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 6,
},
// fileFilter: fileFilter,
});
//adding and update profile image
router
.route("/add/image")
.patch(middleware.checkToken, upload.array("img",5), (req, res) => {
Profile.findOneAndUpdate(
{ username: req.decoded.username },
{
$set: {
img: req.files.path,
},
},
{ new: true },
(err, profile) => {
if (err) return res.status(500).send(err);
const response = {
message: "image added successfully updated",
data: profile,
};
return res.status(200).send(response);
}
);
});
when you upload multiple images, multer create a array of object like this :
req.files : [{…}, {…}, {…}]
that one of the property of objects is path, so there are many way to insert to path in img.
if type of img defined array [] in schema you can do like this in findOneAndUpdate :
{
$set: {
img: req.files.map(file => file.path),
},
}
I'm currently using Postman in which I require to upload two files from two different fields to AWS-S3; this is how it looks like:
This is the API route that I'm calling:
router.route('/').post(uploadThumbnail, uploadVideo, createVideo);
That route calls three functions(which is supposed to return the data requested from Postman):
exports.createVideo = asyncHandler(async (req, res, next) => {
// Add user to req,body
req.body.user = req.user.id;
// Bring files
if (req.file) {
console.log(req.file);
}
});
Here are the two other functions(with the aws upload function); one for thumbnail and a second one for video_url:
const upload = multer({
storage: multerS3({
s3: s3,
bucket: process.env.AWS_BUCKET_NAME,
acl: 'public-read',
key: function(req, file, cb) {
const strOne = process.env.WEBSITE_NAME + '-';
const userId = req.user.id + '-';
const userEmail = req.user.email + '-';
const todaysDate = Date.now().toString() + '.';
const extension = file.mimetype.split('/')[1];
const finalStr = strOne.concat(userId, userEmail, todaysDate, extension);
cb(null, finalStr);
}
})
});
exports.uploadThumbnail = upload.single('thumbnail');
exports.uploadVideo = upload.single('video_url');
Everytime I run the post, Postman throws me this error:
{
"status": "error",
"error": {
"name": "MulterError",
"message": "Unexpected field",
"code": "LIMIT_UNEXPECTED_FILE",
"field": "video_url",
"storageErrors": [],
"statusCode": 500,
"status": "error"
},
"message": "Unexpected field",
"stack": "MulterError: Unexpected field\n at wrappedFileFilter (C:\\xampp\\htdocs\\myporn\\node_modules\\multer\\index.js:40:19)\n at Busboy.<anonymous> (C:\\xampp\\htdocs\\myporn\\node_modules\\multer\\lib\\make-middleware.js:114:7)\n at Busboy.emit (events.js:198:13)\n at Busboy.EventEmitter.emit (domain.js:448:20)\n at Busboy.emit (C:\\xampp\\htdocs\\myporn\\node_modules\\busboy\\lib\\main.js:38:33)\n at PartStream.<anonymous> (C:\\xampp\\htdocs\\myporn\\node_modules\\busboy\\lib\\types\\multipart.js:213:13)\n at PartStream.emit (events.js:198:13)\n at PartStream.EventEmitter.emit (domain.js:448:20)\n at HeaderParser.<anonymous> (C:\\xampp\\htdocs\\myporn\\node_modules\\dicer\\lib\\Dicer.js:51:16)\n at HeaderParser.emit (events.js:198:13)\n at HeaderParser.EventEmitter.emit (domain.js:448:20)\n at HeaderParser._finish (C:\\xampp\\htdocs\\myporn\\node_modules\\dicer\\lib\\HeaderParser.js:68:8)\n at SBMH.<anonymous> (C:\\xampp\\htdocs\\myporn\\node_modules\\dicer\\lib\\HeaderParser.js:40:12)\n at SBMH.emit (events.js:198:13)\n at SBMH.EventEmitter.emit (domain.js:448:20)\n at SBMH._sbmh_feed (C:\\xampp\\htdocs\\myporn\\node_modules\\streamsearch\\lib\\sbmh.js:159:14)\n at SBMH.push (C:\\xampp\\htdocs\\myporn\\node_modules\\streamsearch\\lib\\sbmh.js:56:14)\n at HeaderParser.push (C:\\xampp\\htdocs\\myporn\\node_modules\\dicer\\lib\\HeaderParser.js:46:19)\n at Dicer._oninfo (C:\\xampp\\htdocs\\myporn\\node_modules\\dicer\\lib\\Dicer.js:197:25)\n at SBMH.<anonymous> (C:\\xampp\\htdocs\\myporn\\node_modules\\dicer\\lib\\Dicer.js:127:10)\n at SBMH.emit (events.js:198:13)\n at SBMH.EventEmitter.emit (domain.js:448:20)\n at SBMH._sbmh_feed (C:\\xampp\\htdocs\\myporn\\node_modules\\streamsearch\\lib\\sbmh.js:188:10)\n at SBMH.push (C:\\xampp\\htdocs\\myporn\\node_modules\\streamsearch\\lib\\sbmh.js:56:14)\n at Dicer._write (C:\\xampp\\htdocs\\myporn\\node_modules\\dicer\\lib\\Dicer.js:109:17)\n at doWrite (_stream_writable.js:415:12)\n at writeOrBuffer (_stream_writable.js:399:5)\n at Dicer.Writable.write (_stream_writable.js:299:11)"
}
The function works great but only when sending one single file, it can be either thumbnail or video_url but not both...I need both fields to work.
Any idea on how to fix this?
const s3 = new AWS.S3({
accessKeyId: 'xxxxxxxxx',
secretAccessKey: 'xxxxxxxxx'
});
const uploadS3 = multer({
storage: multerS3({
s3: s3,
acl: 'public-read',
bucket: 'xxxxxxxx',
metadata: (req, file, callBack) => {
callBack(null, { fieldName: file.fieldname })
},
key: (req, file, callBack) => {
var fullPath = 'products/' + file.originalname;//If you want to save into a folder concat de name of the folder to the path
callBack(null, fullPath)
}
}),
limits: { fileSize: 2000000 }, // In bytes: 2000000 bytes = 2 MB
fileFilter: function (req, file, cb) {
checkFileType(file, cb);
}
}).array('photos', 10);
exports.uploadProductsImages = async (req, res) => {
uploadS3(req, res, (error) => {
console.log('files', req.files);
if (error) {
console.log('errors', error);
res.status(500).json({
status: 'fail',
error: error
});
} else {
// If File not found
if (req.files === undefined) {
console.log('uploadProductsImages Error: No File Selected!');
res.status(500).json({
status: 'fail',
message: 'Error: No File Selected'
});
} else {
// If Success
let fileArray = req.files,
fileLocation;
const images = [];
for (let i = 0; i < fileArray.length; i++) {
fileLocation = fileArray[i].location;
console.log('filenm', fileLocation);
images.push(fileLocation)
}
// Save the file name into database
return res.status(200).json({
status: 'ok',
filesArray: fileArray,
locationArray: images
});
}
}
})
};
Make use of file.fieldname and .fields to achieve what you need
function uploadImageToS3(){
const multer = require("multer");
const multerS3 = require("multer-s3");
const uuid = require("uuid").v4;
const path = require("path");
const cloudStorage = multerS3({
s3: s3Client,
bucket: process.env.AWS_PRODUCTS_BUCKET,
acl: "public-read",
metadata: (req, file, cb) => {
if (file.fieldname == "thumbnail") {
const extension = ".jpeg";
cb(null, `${uuid()}${extension}`);
return;
}
if (file.fieldname == "video_url") {
const extension = ".jpeg";
cb(null, `${uuid()}${extension}`);
return;
}
cb(
"fieldName other than avatar and productImages are not supported",
null
);
},
// key: name of the file
key: (req, file, cb) => {
const extension = path.extname(file.originalname);
cb(null, `${uuid()}${extension}`);
},
});
const limitFileSize = { fileSize: 1024 * 1024 * 5 }, // 1Byte -->1024Bytes or 1MB --> 5MB
filterFileType = (req, file, cb) => {
const isAllowedFileType =
file.mimetype == "image/jpeg" ||
file.mimetype == "image/jpg" ||
file.mimetype == "image/png";
if (isAllowedFileType) {
cb(null, true);
return;
}
// To reject this file pass `false`
cb(null, false);
};
const upload = multer({
storage: cloudStorage,
limits: limitFileSize,
fileFilter: filterFileType,
});
return upload;
}
In route, use it like so
router.post(
"/upload",
uploadImageToS3().fields([
{ name: "thumbnail", maxCount: 1 },
{ name: "video_url", maxCount: 1 }
]),
handleImagesUploadController
);
I have a document that contains two fields are of input type="file" and I want to upload both of these on submit.
This post method giving me internal server error 500 on uploading two files but when I upload one file, it is OK.
router.post('/', mediaFiles.uploadSingle('icon_url'), mediaFiles.uploadSingle('background_url'),
async (req, res) => {
name: req.body.name,
icon_url: req.file.path.replace(/\\/g, "/"), // req.file['icon_url']
background_url: req.file.path.replace(/\\/g, "/") // req.file['background_url']
})
you can ignore this MediaFiles class because it provides traditional code to upload images with multer
import multer from "multer";
import path from "path";
class MediaFiles {
private storage = multer.diskStorage({
destination: 'uploads/',
filename: function (req, file, callback) {
callback(
null,
file.originalname.replace(/\.[^/.]+$/, "") + '-' + Date.now() + path.extname(file.originalname))
}
})
uploadSingle(fieldName?: string) {
try {
return multer({
storage: this.storage,
limits: { fileSize: 1024 * 1024 * 1 }, // 1MB = 1024 * 1024 * 1
fileFilter: function (req, file, callback) {
const fileTypes = /jpeg|jpg|png/;
const extName = fileTypes.test(path.extname(file.originalname).toLowerCase());
const mimeType = fileTypes.test(file.mimetype);
if (extName && mimeType) {
callback(null, true)
} else {
callback(new Error('Error: Images Only!'), null)
}
}
}).single(fieldName);
} catch (err) {
console.log(err.message)
}
}
}
export default new MediaFiles();
I don't think you could have two multer objects, I was having the same problem and here is what worked for me.
const storage = multer.diskStorage()
const mediaFiles = multer({
storage:storage })
.fields([{ name: 'icon_url', maxCount: 1 }, { name: 'background_url', maxCount: 1 } ]
router.post('/', mediaFiles, async (req, res) => {
console.log(req.files) // req.files is an array of files
}