Uploading large video file with nodejs, multer and cloudinary - node.js

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).

Related

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)

am not able to upload image from react native to nodejs server even i can upload image from postman,

am using "react-native-image-picker": "^3.2.1", to get images from the library am getting it, but am not able to upload it to nodejs server using multer although I can upload an image from postman it means it should work fine
here is my react native code
this.setState({ uri: response.uri });
const bodyToBe = new FormData({ maxDataSize: 20971520 });
bodyToBe.append('photo', {
type: response.type,
uri: response.uri,
name: response.fileName
})
fetch('http://192.168.10.6:5000/api/open/upload-any-image', {
headers: {
Authorization: `Bearer ${this.props.user.userToken}`,
'Accept': 'multipart/form-data',
'Content-Type': 'multipart/form-data',
},
method: 'POST',
body: bodyToBe
})
.then(response => {
console.log(response, "this is response");
response.json().then(res => console.log(res, "its is")).catch(err => console.log(err, "here you go"));
})
.catch(e => {
console.log(e.toString(), "error")
});
and server-side multer config is
'use strict;
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads');
},
filename: function (req, file, cb) {
cb(null, Date.now() + file.originalname);
}
});
const fileFilter = (req, file, cb) => {
// reject a file
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
return cb(null, true);
} else {
return cb(null, false);
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 25
},
fileFilter: fileFilter
});
module.exports = upload;
and the express route is
console.log("request =>" ,req.files)
return openService
.uploadImage(req.body, req.files, req.user)
.then((data) => {
res.send(RS.successMessage(data))
})
.catch((err) => next(err));
});
Thanks in advance for helping.

How to loop into multiple fieldname to get the files.path

I have Node.js app, served by express, with my frontend being made with React.js. My issue is i got to different fieldname for my images. Cover Photo and Avatar Photo. I'm having hard time to figure out how to loop into to different fieldname and get the path of the image. The result that i want is the backend will res.send(path of the image either avatar or cover or both of them).
// imageuploadroutes
import express from 'express';
const router = express.Router();
import multer from 'multer';
import path from 'path';
const storage = multer.diskStorage({
destination: function (req, file, cb) {
if (file.fieldname === 'coverPhoto') {
cb(null, 'public/images/cover');
} else {
cb(null, 'public/images/avatar');
}
},
filename: function (req, file, cb) {
cb(
null,
`${file.fieldname}-${Date.now()}${path.extname(file.originalname)}`
);
},
});
const upload = multer({
storage,
limits: {
fileSize: '2mb',
},
fileFilter: (req, file, cb) => {
if (
file.mimetype == 'image/png' ||
file.mimetype == 'image/jpg' ||
file.mimetype == 'image/jpeg'
) {
cb(null, true);
} else {
cb(null, false);
return cb(new Error('Only .png, .jpg and .jpeg format allowed!'));
}
},
});
router.post(
'/user/profile',
upload.fields([
{
name: 'coverPhoto',
maxCount: 1,
},
{
name: 'avatarPhoto',
maxCount: 1,
},
]),
function (req, res) {
var file = req.files[Object.keys(req.files)[0]];
console.log(file)
}
);
export default router;
Result
[
{
fieldname: 'avatarPhoto',
originalname: 'Screen Shot 2021-03-02 at 11.49.56 AM.png',
encoding: '7bit',
mimetype: 'image/png',
destination: 'public/images/avatar',
filename: 'avatarPhoto-1614704247624.png',
path: 'public/images/avatar/avatarPhoto-1614704247624.png',
size: 597941
}
]
but the problem is I can't get the .path
The file is an array, you should iterate over it:
var file = req.files[Object.keys(req.files)[0]];
console.log(file);
file.forEach(fileData => {
console.log(fileData.path);
})

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

React, Node, Mongodb : My image file is not being served from backend and trying to render it on the React side throws 404 not found

I am uploading the image from React front end.
constructor(props) {
super(props);
this.state = {
uploadRecipesFormData: {},
selectedFile: null,
redirect : null,
};
}
uploadRecipe = () => {
const formData = new FormData();
formData.append('file', this.state.selectedFile, this.state.selectedFile.name);
for(const key in this.state.uploadRecipesFormData){
formData.append(key, this.state.uploadRecipesFormData[key]);
}
axios.post(`http://localhost:3000/recipes`, formData, {})
.then(res => {
alert(res.data);
this.setState({
redirect: "/uploaded-recipes"
});
})
.catch(err => console.error(err));
}
onChangeImageHandler = e => {
this.setState({
selectedFile: e.target.files[0]
});
}
handleChange = e => {
e.preventDefault();
this.handleInputChange(e.target.name, e.target.value);
}
handleInputChange = (key, value) => {
this.setState({
uploadRecipesFormData: {
_id: uuidv4(),
...this.state.uploadRecipesFormData,
addedBy: 'guest',
[key]: value
}
});
}
submitRecipe = e => {
e.preventDefault();
this.uploadRecipe();
}
On backend Im receiving the image along with other form data:
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'public/images')
},
filename: (req, file, cb) => {
cb(null, `${file.originalname} `)
}
});
const upload = multer({ storage: storage }).single('file');
/*
Endpoint: /recipes
Outcome: Add recipe
*/
router.post('/', (req, res) => {
upload(req,res, err => {
if(err instanceof multer.MulterError) {
return res.status(500).json(err);
}
else if(err) {
return res.status(500).json(err);
}
console.log(req.file.filename);
console.log(req.body);
const newRecipeData = new Recipe({
_id: req.body._id,
name: req.body.name,
ingredients: req.body.ingredients.split(','),
instructions: req.body.instructions,
cuisine: req.body.cuisine || '',
image: `http://localhost:3000/images/${req.file.filename}` || '',
addedBy: req.body.addedBy || ''
});
newRecipeData.save()
.then(result => {
res.status(201).send('Recipe added successfully!');
})
.catch(err => console.log(err));
});
});
The data is being stored in MongoDb with path to public directory to image. However, this file is being stored as a binary file instead of an image file.
I have enabled express static in my app.js:
app.use(express.static('public'));
I am able to see the file in public/images folder but its a binary file due to which when I try to access it from my react front end it throws 404 error. I then manually tried adding an actual image to public/images folder and the it worked. How could I preserve the file type while receiving file data from front end ? Answers online are not clear to me of what I am trying to do.
Your code seems okay, however, I can spot a little bug in this block of code that might be causing the error:
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'public/images')
},
filename: (req, file, cb) => {
cb(null, `${file.originalname} `)
}
});
There is an extra space after ${file.originalname} in this
cb(null, `${file.originalname} `) statement. That extra space would change the file name, consequently, it appears as binary and you can't access it from the front end. Try removing that space and see if it works.

Resources