how to upload image files to server and MongoDB - node.js

React.js Node.js Express.js Axios Multer MongoDB Mongoose
Wrote this form to register users, the user enters name, email, password, and a file for later use as profile pic/Avatar.
When i tested the form on Postman the opposite has happened, the file was uploaded to the public/uploads/images folder but it didnt post to the user with the rest of the registration data i entered.
But when i tested the form on localhost domain the image filename was saved on the user's Avatar value MongoDB nut was'nt uploaded to the servers folder i chose to upload my uploaded files.
wanted end result:
to register user to websites database with all parameters, uploading the image to the server and image filename to users avatar data
Register Route
router.post("/register", upload.single("avatar"), async (req, res) => {
try {
const validatedValue = await validateRegisterSchema(req.body);
const user = await findUserByEmail(validatedValue.email);
if (user) throw "try different email";
const hashedPassword = await createHash(validatedValue.password);
validatedValue.password = hashedPassword;
await createNewUser(validatedValue);
res.status(201).json({ msg: "user created"});
} catch (error) {
res.status(400).json({ error });
}
});
Multer
destination: (req, file, cb) => {
cb(null, "./public/uploads/images/");
},
filename: function (req, file, cb) {
crypto.pseudoRandomBytes(16, function (err, raw) {
if (err) return cb(err);
cb(null, file.originalname);
});
},
});
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,
limit: {
fileSize: 1024 * 1024 * 10,
},
fileFilter,
});
User Model
const Schema = mongoose.Schema;
const usersSchema = new Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
wishList: { type: Array },
isAdmin: { type: Boolean, default: false },
avatar: { type: String },
});
const Users = mongoose.model("users", usersSchema);
const createNewUser = (userData) => {
const newUser = new Users(userData);
return newUser.save();

Solved
prior code:
const handleAvatarChange = (ev) => {
let newUserInput = JSON.parse(JSON.stringify(userInput));
newUserInput[ev.target.name] = ev.target.files[0].name;
setUserInput(newUserInput);
};
sulution:
const handleAvatarChange = (ev) => {
let newUserInput = JSON.parse(JSON.stringify(userInput));
newUserInput[ev.target.name] = ev.target.files[0]; // removed ".name" left it as file object
setUserInput(newUserInput);
};

Related

Upload multiple files in node.js using multer

I am creating a node.js app in which there will be an option of uploading the documents.
The front-end Scenario: There will be three fields 1) profile picture 2) user's ID card no 1 (eg. PAN card) 3) user's Id card no 2 (eg. AADHAR card) -> this all field will be in a single page
Now the concern is whenever user uploads the documents I want to save it in a such a way that profile_picture should be considered as 0, user's ID card no 1 should be considered as 1 and so on.
The user schema:
const userSchema = new mongoose.Schema(
{
profile_picture: { type: Array },
documents: {
type: Array,
},
...
{
timestamps: true,
}
);
The uploadDocument service function:
exports.uploadDocuments = async (_id, file) => {
let document = await User.findByIdAndUpdate(
{ _id },
{
$set: { registration_process_value: 99 },
$push: file, //The actual problem is here
},
{ new: true }
);
return document;
};
the controller:
exports.uploadDocuments = catchAsync(async (req, res) => {
try {
check_user = await getUser.getUser(req.data.id);
if (!check_user) return res.status(404).json({ message: "User not found" });
if (check_user.registration_process_value >= 4) {
let uploadDocument = await userService.uploadDocuments(
check_user.id,
req.files
);
return res.status(200).json({ data: uploadDocument });
}
return res.status(404).json({ message: "You are not a verified user" });
} catch (error) {
return res.status(400).json({ message: error });
}
});
The multer function:
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./src/uploads/");
},
filename: function (req, file, cb) {
cb(
null,
file.fieldname + "-" + Date.now() + path.extname(file.originalname)
);
},
});
var upload = multer({
storage: storage,
fileFilter: function (req, file, cb) {
var filetypes = /jpeg|jpg|png|pdf/;
var mimetype = filetypes.test(file.mimetype);
var extname = filetypes.test(path.extname(file.originalname).toLowerCase());
if (mimetype && extname) {
return cb(null, true);
}
cb("Please upload valid file");
},
}).fields([
{ name: "documents", maxCount: 2 },
{ name: "profile_picture", maxCount: 1 },
]);
I literally have no idea how to accomplish that. Can anyone assist me here?

Image url in mongodb by using multer and local storage of machine

I want to store the image URL in my MongoDB collection using multer and machine storage. I tried to follow one tutorial but it is not generating the correct URL I am posting my code here too. Can someone please guide me? I am very new to storing data in the database.
This is my model file:
const mongoose = require(‘mongoose’);
const User = mongoose.model(‘User’, new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
image: {
type: String
}
}));
exports.User = User;
This is my multer middleware:
const store = multer.diskStorage({
destination: function (req, file, cb) {
fs.mkdir(path, { recursive: true}, function (err) {
if (err) return cb(err);
cb(null, “uploads/photos”);
});
},
filename: function (req, file, cb) {
const name = file.originalname.toLowerCase().split(’ ‘).join(’_’);
cb(null, name + ‘-’ + Date.now());
}
});
const upload = multer({ storage: store }).single(‘image’);
This is my post router:
function CreateUser(req, res) {
const url = req.protocol + ‘://’ + req.get(“host”);
let user = new User(
{
name: req.body.name,
email: req.body.email,
image: url + ‘/images/’ + req.file.filename
}
);
user.save()
.then(data => {
res.send(data);
}).catch(err => {
res.status(500).send({
success: false,
message: err.message || “Some error occurred while creating the user.”
});
});
};
router.post(’/create’, [upload], CreateUser);
And in last I use the path npm module to join it in my server.js file so that I can get it to frontend also.
app.use("/images", express.static(path.join("uploads/photos")));
When I run in postman it is generating this URL:
http://localhost:5000/images/picture.png-1655124752174
And I can't access this URL, Can anyone please help me with this code I am stuck here?
I followed this tutorial to make this:
https://www.javatpoint.com/working-with-file-url-in-mean-stack

Uploaded image with Multer using MERN stack disappears after an hour or two

I am trying to upload an image using multer. The site already deployed in vercel.com which comes with free hosting. The code works well and the images can definitely be uploaded and be seen after the successful upload. However, after 1 hour or two, the uploaded images are gone.
Here is the code in my API.
//--------------------------------- ROUTE
const multer = require('multer');
const Storage = multer.diskStorage({
destination: 'uploads',
filename:(req,file,cb)=>{
cb(null, file.originalname);
},
});
const upload = multer.({
storage:Storage
}).single('testImage')
router.post('/upload', (req,res) => {
upload(req,res,(err)=>){
if(err){
console.log(err)
}else {
const newImage = new
}
}
})
const storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, path.join(__dirname, '../images/'))
},
filename: function(request, file, callback){
callback(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname))
}
})
const upload = multer({
storage: storage,
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('/create', AUTHENTICATE, AUTHORIZE, upload.single('image'), (req, res) => {
const url = req.protocol + '://' + req.get('host')
ProductController.INSERTPRODUCT(req.body, req.file, url).then(resultFromController => res.send(resultFromController))});
//--------------------------------- CONTROLLER
module.exports.INSERTPRODUCT = (productData, reqFile, url) => {
let newProduct = new Product({
name: pname,
description: desc,
price: price,
isActive : act,
image: url + "/images/" + reqFile.filename
});
return newProduct.save().then((product, err) => {
if (product) {
return product;
} else {
return err;
};
}).catch(error => error)
};
const mongoose = require('mongoose');
//--------------------------------- SCHEMA
const productSchema = new mongoose.Schema({
name: {
type: String,
unique: true,
required: [true, 'Product Name is Required']
},
description: {
type: String,
unique: true,
required: [true, 'Description is Required']
},
price: {
type: Number,
required: [true, 'Product Price is Required']
},
image: {
data:Buffer,
contentType: String
},
isActive: {
type: Boolean,
default: true
},
createdOn: {
type: Date,
default: Date.now
}});
module.exports = mongoose.model('Product', productSchema);
The thing is, it was deployed in heroku and the front end is in vercel. I asked a few friends and they said that since it requires a dedicated storage, it is an add on to hosting platforms already. Which means the hosted site has to be paid. I was just thinking thay maybe, maybe there is a way that the uploaded images won't be gone after being uploaded without having an add on to hosting platforms.

How to add user profile picture in schema

I have to develop an application in which the user has a profile picture in his schema. I am using gridfs (MongoDB). how can I access the specific picture of a user? if I upload a picture it will show on every user profile how can I make it unique?
// This is my user schema I want to add a profile picture here but I don't know how to do it?
const Joi = require('joi');
const mongoose = require('mongoose');
const config = require('config');
const User = mongoose.model('User', new mongoose.Schema({
name: {
type: String,
required: true,
minlength: 5,
maxlength: 50
},
email: {
type: String,
required: true,
minlength: 5,
maxlength: 255,
unique: true
},
password: {
type: String,
// required: true,
minlength: 5,
maxlength: 1024
},
isVerified: {
type: Boolean,
default: false
}
}));
//This is my post image API I am using gridfs as a middle ware in it
const uploadFiles = async (req, res) => {
try {
await upload(req, res);
console.log(req.file);
if (req.file == undefined) {
return res.send({
message: "You must select a file.",
});
}
return res.send({
message: "File has been uploaded.",
});
} catch (error) {
console.log(error);
return res.send({
message: "Error when trying upload image: ${error}",
});
}
};
//Gridfs middleware code
const util = require("util");
const multer = require("multer");
const { GridFsStorage } = require("multer-gridfs-storage");
const dbConfig = require('../config/db')
var storage = new GridFsStorage({
url: dbConfig.url + dbConfig.database,
options: { useNewUrlParser: true, useUnifiedTopology: true },
file: (req, file) => {
const match = ["image/png", "image/jpeg"];
if (match.indexOf(file.mimetype) === -1) {
const filename = `${Date.now()}-image-${file.originalname}`;
return filename;
}
return {
bucketName: dbConfig.imgBucket,
filename: `${Date.now()}-image-${file.originalname}`
};
}
});
var uploadFiles = multer({ storage: storage }).single("file");
var uploadFilesMiddleware = util.promisify(uploadFiles);
module.exports = uploadFilesMiddleware;
//Image get API request
const getListFiles = async (req, res) => {
try {
await mongoClient.connect();
const database = mongoClient.db(dbConfig.database);
const images = database.collection(`${dbConfig.imgBucket}.files`);
const cursor = images.find({});
if ((await cursor.count()) === 0) {
return res.status(404).send({
message: "No files found!",
});
}
let fileInfos = [];
await cursor.forEach((doc) => {
fileInfos.push({
name: doc.filename,
url: baseUrl + doc.filename,
});
});
return res.status(200).send(fileInfos);
} catch (error) {
return res.status(500).send({
message: error.message,
});
}
};
//Routes
router.post('/upload', uploadFiles);
router.get('/files', getListFiles)
Images are stored in the image bucket and also I can get them too but I do not know how to get them as a user-specified image.
Add profilePictureURL as a string type in schema.
profilePictureURL: { type: String }
Save Profile Picture to User's Profile picture or any folder with
current date or user's _id or any unique Value to make it unique.
For Ex. if you save your image into public/user/profile_picture
profilePictureURL = 'public/user/profile_picture/'+user._id
Retrieve that profilePictureURL and access them using
baseURL + profilePictureURL
Mongo DB generates _id to each and every obj. You can store your image URL on the User scheme and while fetching the user you can get a specific image based on the User.
You can also use Populate fields(Like Joins) on User schema where you can store image id on user and while fetching you can populate entire image object with User itself.
You need to update your schema with the references.
const UserSchema = Schema({
_id: Schema.Types.ObjectId,
name: String,
stories: [{ type: YourIdType(Eg. Schema.Types.ObjectId), ref: 'YourImageCollectionName' }]
});

How can i if dont send image implement default image on my user?

Hi everyone My users have profile picture but its not required,I dont want. So I need if user send me image while register or updated i take this picture and save, so i can this. My problem if user dont send me image i cant give default image
Its my controller code :
//User Register Controller
const register = async (req, res, next) => {
try {
const {
name,
surname,
username,
phoneNumber,
email,
password,
gender,
profilPicture,
birtDate,
} = req.body;
bcrypt.hash(password, 8, async (err, hash) => {
try {
const user = new User({
name,
surname,
username,
phoneNumber,
email,
password: hash,
gender,
profilPicture: 'http://localhost:4000/' + req.file.path || 'http://localhost:4000/public/images/profilePictures/defaultProfilePicture.png',
birtDate,
});
const createdUser = await user.save();
const token = await createToken(createdUser);
res.status(200).json({ token });
} catch (err) {
res.json(err);
}
});
} catch (error) {
res.json({ message: error.message });
}
};
It's my middleware multer :
const multer = require('multer');
const stor = multer.diskStorage({
destination:function(req,file,cb) {
cb(null,'./public/images/profilePictures')
},
filename:function(req,file,cb){
cb(null,'profilePicture-'+new Date().toISOString().replace(/:/g, '-')+file.originalname);
}
})
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: stor,
limits: {
fileSize: 1024 * 1024 * 5,
},
fileFilter: fileFilter,
});
module.exports = upload;
My route :
router.post('/register', upload.single('profilPicture'), userController.register);
One way you could do it is set a default within your model
const mongoose = require("mongoose")
const UserSchema = new mongoose.Schema({
profilePicture: {
type: String,
default: 'defaultProfilePicture.png'
}
})
module.exports = mongoose.model("Users", UserSchema)
This way if the user didnt upload a profile picture it would be set to defaultProfilePicture.png
You wouldnt need
profilPicture: 'http://localhost:4000/' + req.file.path || 'http://localhost:4000/public/images/profilePictures/defaultProfilePicture.png',
Just
profilPicture: 'req.file.path'
saving the full URL is never a good idea because you might choose to change it in the future like to remote file store or host your project then the URL will no longer be http://localhost:4000/. You'd ideally save the images unique name and extension.
just try like this :
profilPicture: (req.file) ? 'http://localhost:4000/' + req.file.path : 'http://localhost:4000/public/images/profilePictures/defaultProfilePicture.png';

Resources