Error uploading multiple images in node.js - 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),
},
}

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)

React Native uploading multiple images with fetch to node js server with express and multer

I am trying to upload multiple images to my nodejs server where i am using multer to parse form-data,
but the problem is, when i am trying to upload images through postman it gets successfully upload but with react native fetch api it didn't work.
Here is my code:
React Native:
import React from "react";
import { View, Text, Button } from "react-native";
export default function imageUpload(props) {
function upload() {
const photo1 = {
uri: "https://html5box.com/html5gallery/images/Swan_1024.jpg",
type: "image/jpeg",
name: "photo1.jpg",
};
const photo2 = {
uri:
"https://www.canva.com/wp-content/themes/canvaabout/img/colorPalette/image4.jpg",
type: "image/jpeg",
name: "photo2.jpg",
};
const photos = [photo1, photo2];
const form = new FormData();
for (let photo in photos) {
form.append("image", photos[photo]);
}
console.log(form);
fetch("http://192.168.31.208:3000/uploads", {
body: form,
method: "POST",
headers: {
"Content-Type": "multipart/form-data",
// Accept: "application/x-www-form-urlencoded",
},
})
.then((response) => response.json())
.then((responseData) => {
console.log("Response:" + responseData);
})
.catch((error) => {
console.log(error);
});
}
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Image Upload</Text>
<Button title="Test" onPress={upload} />
</View>
);
}
Server Code:
const express = require("express");
const http = require("http");
const multer = require("multer");
const path = require("path");
const storage = multer.diskStorage({
destination: "./uploads",
filename: function (req, file, cb) {
cb(
null,
file.fieldname + "-" + Date.now() + path.extname(file.originalname)
);
},
});
const upload = multer({
storage: storage,
limits: { fileSize: 52428800 },
fileFilter: function (req, file, cb) {
checkFileType(file, cb);
},
}).array("image");
function checkFileType(file, cb) {
const fileType = /jpeg|jpg|png|gif/;
const extname = fileType.test(path.extname(file.originalname).toLowerCase());
const mimeType = fileType.test(file.mimetype);
if (extname && mimeType) {
return cb(null, true);
} else {
cb("Error: FILE_SHOULD_BE_TYPE_IMAGE");
}
}
// Initialise app
const app = express();
app.use(express.static("./uploads"));
app.post("/uploads", (req, res) => {
upload(req, res, (err) => {
if (err) {
res.status(400).json({
error: err,
});
} else {
console.log(req);
res.status(200).json({
// name: req.body.name,
// image: req.file,
message: "Testing",
});
}
});
});
app.get("/upload", (req, res, next) => {
res.status(200).json({
message: "TestComplete",
});
});
const port = 3000;
app.listen(port, () => {
console.log(`Server started on port number: ${port}`);
});
Console Output Frontend:
FormData {
"_parts": Array [
Array [
"image",
Object {
"name": "photo1.jpg",
"type": "image/jpeg",
"uri": "https://html5box.com/html5gallery/images/Swan_1024.jpg",
},
],
Array [
"image",
Object {
"name": "photo2.jpg",
"type": "image/jpeg",
"uri": "https://www.canva.com/wp-content/themes/canvaabout/img/colorPalette/image4.jpg",
},
],
],
}
Network request failed
- node_modules\whatwg-fetch\dist\fetch.umd.js:473:29 in xhr.onerror
- node_modules\event-target-shim\dist\event-target-shim.js:818:39 in EventTarget.prototype.dispatchEvent
- node_modules\react-native\Libraries\Network\XMLHttpRequest.js:574:29 in setReadyState
- node_modules\react-native\Libraries\Network\XMLHttpRequest.js:388:25 in __didCompleteResponse
- node_modules\react-native\Libraries\vendor\emitter\EventEmitter.js:190:12 in emit
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:436:47 in __callFunction
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:111:26 in __guard$argument_0
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:384:10 in __guard
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:110:17 in __guard$argument_0
* [native code]:null in callFunctionReturnFlushedQueue
Thanks for any help
If you are trying to upload an image from mobile (Android/iOS) you can't pass it as URL. You should send a local file path/uri

Unable to upload image/video files from react-native to nodejs

I am using expo-image-picker in react-native to pick image/video files and multer in nodejs as middleware to download files in directory /public/upload. When i am uploading file along with other parameters from react-native, multer is unable to detect a file present in req.body and hence not downloading any file.
Here is my react-native code using axios
pickImage = async () => {
try {
let options = {
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 1,
// base64:true
}
let result = await ImagePicker.launchImageLibraryAsync(options)
if (!result.cancelled) {
this.setState({ content: result })
}
} catch (E) {
console.log("error in picking image:", E)
}
}
createFormData = (response) => {
const photo = {
uri: response.uri,
type: response.type,
name: "my-img.jpg",
};
const form = new FormData();
form.append('acivityFile',photo);
return form;
};
handleSubmit = async () => {
if (this.state.content) {
const formData = this.createFormData(this.state.content)
console.log("form data:", formData)
try {
const res = await axios.post('http://393ad751391b.ngrok.io/activities',
{
title: "This is the title",
description: "This is a description",
eventType: "LOST & FOUND",
file: formData
},
{
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data'
},
},
)
console.log("res:", res.data);
} catch (err) {
console.log("err in post axios:",err)
}
}
}
Here is my route file handling http requests in server-side
const express = require('express');
const Upload = require('./../utils/multerSetup');
const activityController = require('../Controllers/activityController');
const router = express.Router();
router
.route('/')
.get(activityController.getAllActivities)
.post(
Upload.single('activityFile'),
activityController.addActivity
);
Here is my multerSetup.js file in server-side
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/uploads/');
},
filename: function (req, file, cb) {
const ext = file.mimetype.split('/')[1];
cb(null, file.fieldname + '-' + Date.now() + '.' + ext);
},
});
const upload = multer({ storage });
module.exports = upload;
Here is my activityController.js file in server-side
const Activity = require('./../modals/activityModel');
const User = require('./../modals/user');
exports.getActivity = async (req, res, next) => {
console.log('here');
const activity = await Activity.findById(req.params.id);
res.status(200).json({
status: 'success',
data: {
activity,
},
});
};
exports.addActivity = async (req, res, next) => {
if (req.file) {
let file = {
Ftype: req.file.mimetype.split('/')[0],
name: req.file.filename,
};
req.body.file = file;
}
if (!req.body.location) {
req.body.location = {
coordinates: ['77.206612', '28.524578'],
};
}
if (req.body.votes) {
req.body.votes.diff = req.body.votes.up - req.body.votes.down;
}
req.body.creator = "12345" || "req.userId";
const activity = await Activity.create(req.body);
res.status(201).json({
status: 'success',
data: {
activity,
},
});
};
Also when Content-type:'multipart/form-data, then server console throws Error: Multipart:Boundary not found. When i use Content-type:'application/json', then multer does not work.
I just want to know what is the correct way of uploading files with additional parameters from react-native to nodejs multer. Any suggestions would be a great help!

upload files from different fields in a form using multer

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
}

Resources