Multer upload files into array of objects - node.js

I have an array of objects like this:
data: [
{"image": File, "certificate": File},
{"image": File, "certificate": File}
]
The way I'm taking to read the files on the server is doing a loop before send the POST request:
...
const fd = new FormData();
fd.append('image', data[0].image);
fd.append('image', data[1].image);
fd.append('certificate', data[0].certificate);
fd.append('certificate', data[1].certificate);
this.http.post('http://localhost:3000/upload', fd, options);
Then on the server side I do:
upload.fields([{ name: 'image', maxCount: 2 }, { name: 'certificate', maxCount: 2 }])
So my question is... Is there a way that if I send the array like:
fd.append('data', data)
so multer can take the files that are into the data field?

For anyone still struggling with this, this is what worked for me
Backend Code:
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, path.join(__dirname, './images/'))
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now() + file.originalname)
}
});
const upload = multer({ storage: storage })
app.post('/generate', upload.fields([{ name: 'layer1', maxCount: 6 }, { name: 'layer2', maxCount: 6 }, { name: 'layer3', maxCount: 6 }]), function (req, res) {
console.log(req.files)
})
Client request:
const formdata = new FormData();
var myHeaders = new Headers();
myHeaders.append("Accept", "application/json");
for (const layer of layers) {
for (const img of layer.files)
formdata.append(`layer${layer.id}`, img)
}
const requestOptions = {
method: 'POST',
headers: myHeaders,
body: formdata,
redirect: 'follow'
};
fetch("http://localhost:5000/generate", requestOptions)

Related

Uploading large video file with nodejs, multer and cloudinary

My code works very well for small videos up to 50MB however when videos weight more than 50MB, it uploads the video but I dont get any cloudinary url hence the video is not loading in my frontend part . I am using nodejs and multer in my backend with cloudinary as storage and react as my frontend.
Any suggestion ?
Cloudinary config
require("dotenv").config();
const cloudinary = require("cloudinary");
cloudinary.config({
cloud_name: process.env.CLOUD_NAME ,
api_key: process.env.API_KEY ,
api_secret: process.env.API_SECRET ,
});
exports.uploads = (file) => {
return new Promise((resolve) => {
cloudinary.uploader.upload(
file,
(result) => {
resolve({ url: result.url, id: result.public_id });
},
{ resource_type: "auto" }
);
});
};
Video controller
const Video = require("../models/videoModel"),
cloud = require("../config/cloudinaryConfig");
module.exports = {
// Create action for a new video
create: (req, res, next) => {
// First check if the file exists in the Database
let test = {
name: req.files[0].originalname,
url: req.files[0].path,
id: "",
};
console.log(req.files[0].originalname);
Video.find({ name: test.name }, (err, cb) => {
if (err) {
res.json({
error: true,
message: `There was a problem uploading the video because: ${err.message}`,
});
} else {
let file = {
name: req.files[0].originalname,
url: req.files[0].path,
id: "",
};
cloud
.uploads(file.url)
.then((result) => {
Video.create({
name: req.files[0].originalname,
url: result.url,
id: result.id,
});
})
.then((result) => {
res.json({
success: true,
data: result,
});
})
.catch((err) => {
res.json({
error: true,
message: err.message,
});
});
}
});
},
};
Multer config
const multer = require("multer"),
path = require("path");
//multer.diskStorage() creates a storage space for storing files.
const imageStorage = multer.diskStorage({
destination: (req, file, cb) => {
if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
cb(null, path.join(__dirname, "../files"));
} else {
cb({ message: "This file is not an image file" }, false);
}
},
filename: function (req, file, cb) {
cb(null, file.originalname);
},
});
const videoStorage = multer.diskStorage({
destination: (req, file, cb) => {
if (file.mimetype === "video/mp4") {
cb(null, path.join(__dirname, "../files"));
} else {
cb({ message: "This file is not in video format." }, false);
}
},
filename: (req, file, cb) => {
cb(null, file.originalname);
},
});
module.exports = {
imageUpload: multer({ storage: imageStorage }),
videoUpload: multer({ storage: videoStorage }),
};
When uploading files to Cloudinary, the maximum size of the request body can be 100MB. Any request that is larger than this would receive the 413 error you are seeing. To upload files larger than 100MB, these need to be sent in chunks.
Since you are using the Cloudinary NodeJS SDK, you can update your code to use the upload_large method for your uploads instead of the regular upload method.
The upload_large method should be used for all files >100MB as it splits the file and uploads it in parts automatically for you. That said, you can also use this uplaod_large method for all files, even if they are small in filesize and those would work as well.
It takes the exact same parameters as the upload method and also optionally accepts a chunk_size (default 20MB).

Upload Image and PDF in rest API using node js with mongoose

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)

Error uploading multiple images in node.js

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

How to rename my originalname when using multer memoryStorage?

i am trying to rename my file originalname when using multer memoryStorage. I am using multer to upload an array of files and when i console.log(req.files) i get:
{
fieldname: 'images',
originalname: 'snake.jpg',
encoding: '7bit',
mimetype: 'image/jpeg',
buffer: <Buffer ff d8 38134 more bytes>,
size: 38184
}
The reason i want to rename my originalname is i am storing the images in AWS S3 and if the images have the same name it gets updated not added as a new image.
I have tried doing so by adding the date next to the originalname when storing in database but then originalname does not change when the images get added in bucket.
Here is my code:
posts.js
const storage=multer.memoryStorage()
const upload = multer({
storage: storage,
limits: { fieldSize: 25 * 1024 * 1024 },
});
router.post(
"/",
[ upload.array("images", config.get("maxImageCount")),
imageResize,
],
async (req, res) => {
const paths = await req.files.map((file) => ({ originalName: file.originalname + "-" + new
Date().toISOString() + "-" + uuidv4()}));
await Post.create({
title: req.body.title,
userId: req.body.userId,
Post_Images: paths.map((x) => ({ images: x.originalName })),
},
{
include: [Post_Image] }).then(
res.status(201).send())
imageResize.js
const sharp = require("sharp");
require("dotenv").config();
const AWS = require('aws-sdk')
const s3 = new AWS.S3({
accessKeyId: process.env.AWS_ID,
secretAccessKey: process.env.AWS_SECRET,
region:process.env.AWS_REGION
})
module.exports = async (req, res, next) => {
const images = [];
const resizePromises = req.files.map(async (file) => {
console.log(file)
await sharp(file.buffer)
.resize(2000)
.jpeg({ quality: 50 })
.toBuffer()
.then(resized=>s3.upload({
Bucket:process.env.AWS_BUCKET,
Key:file.originalname + "_full.jpg",
Body:file.buffer,
ACL: 'public-read'
}).promise()),
await sharp(file.buffer)
.resize(100)
.jpeg({ quality: 30 })
.toBuffer()
.then(resized=>s3.upload({
Bucket:process.env.AWS_BUCKET,
Key:file.originalname + "_thumb.jpg",
Body:file.buffer,
ACL: 'public-read'
}).promise())
images.push(file.originalname);
});
await Promise.all([...resizePromises]);
req.images = images;
next();
};
in other words, i am trying to change my originalname in my req.files to
originalname + "-" + new Date().toISOString() + "-" + uuidv4()
OR
How do i keep the uuid and the date the same in my posts.js and imageResize.js?

Why can not find the server route of an express server from react native when trying to upload a file?

i have configured an express server that use multer as a middleware in order to upload images to GCS, it works as expected and wanted when making the respective request from insomnia, but when trying to do it from my project in react native, i can not even get connected to that route, and actually i do not know the reason and what else to try.
this is the code from the server:
const storage =
multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, Date.now() + path.extname(file.originalname))
}
})
const upload = multer({ storage: storage })
app.listen(3000);
app.use(router)
router.post('/uploadImage', upload.single('file'), async function (req, res) {
console.log("se conectaron a /uploadingImage")...
This is code from the frontend in react native:
_pickImage = async () => {
this.getPermissionAsync()
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
});
state = {
search: '',
};
this.uploadImage(`http://192.168.1.69:3000/uploadImage`, {
file: {
height:result.height,
type: result.type,
uri: result.uri,
width: result.width,
}
}).then(r => {
});
if (!result.cancelled) {
this.setState({ image: result.uri });
}
};
async uploadImage(url, data) {
let options = {
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data'
},
method: 'POST'
}
options.body = new FormData();
for (let key in data) {
options.body.append(key, data[key]);
}
return fetch(url, options)
.then(async response => {
return response.json()
.then(responseJson => {
return responseJson;
});
});
}
My server have never log in the console the first line in the route ("se conectaron a /uploadingImage")

Resources