How to get files uploaded via multer using mongoose? - node.js

I'm using the below function to get the files uploaded by multer in mongodb.The request is returning empty array.
exports.getPhotos = async (req, res) => {
const photos = await Photo.find()
.then(photos => {
res.status(200).json(photos);
})
.catch(err => res.status(500).json({message: "Something went wrong"}));
};
and this is the schema of the image. Is there any way to get the files without specifying the schema?
const mongoose = require("mongoose");
const {ObjectId} = mongoose.Schema;
const photoSchema = new mongoose.Schema({
lenght: {
type: String,
},
chunkSize: {
type: String,
required: true
},
uploadDate: {
type: Date,
},
filename: {
type: String,
},
md5: {
type: String,
},
contentType: {
type: String,
},
});
module.exports = mongoose.model("Photo", photoSchema);

i use gridfs so it could upload larger files too . a piece of sample code below
//Connecting to mongo
const conn = mongoose.createConnection(mongoURI);
//Init gfs
let gfs;
conn.once('open', ()=>{
gfs = GridFsStream(conn.db, mongoose.mongo);
gfs.collection('uploads');
})
//Creating Storage engine
const storage = new GridFsStorage({
url:mongoURI,
file: (req, file) => {
return new Promise((resolve, reject)=>{
crypto.randomBytes(16,(err, buf)=>{
if(err){
return reject(err)
}
const fileName = buf.toString('hex') + path.extname(file.originalname)
//bucket name should match the collection name
const fileInfo = {
filename:fileName,
bucketName:'uploads'
}
resolve(fileInfo);
})
})
}
})
const upload = multer({storage})
now use this upload const in your paths like the one below there are a few methods for upload like array , single and ... depends on number of files you are uploading . the 'uploadedFile' argument is the name of the file input and you should be consider setting it in your frontend.
app.post('/',upload.single('uploadedFile'),(req, res)=>{
res.json('file uploaded')
})
this upload middleware adds a files to your request which you can use to put file names in your database and later fetch it by that uniqe names with a route like the one below .
app.get('/:filename', (req, res)=>{
gfs.files.findOne({filename:req.params.filename},(err,file)=>{
if(!file || file.length === 0){
return res.status(404).json({
err:'No file Exists'
})
}
const readStream = gfs.createReadStream(file.filename);
readStream.pipe(res)
})
})

Related

Error: "Undefined". Getting Undefined while using multer multiple file uploads. Tried most options including restart and npm reinstalling

I am trying to upload multiple images using a get request to update the initial post request that accepts a single upload. Not able to upload multiple images despite using the "files" as suggested in the docs and on here. The (file.filename) comes back undefined for some reason. Also tried changing the base path manually instead of getting it dynamically. Did not work. The images are uploaded to the __dir but shows up as undefined in the database and the postman put console.
const express = require("express");
const { Category } = require("../models/category");
const router = express.Router(); //comes with express
const {Product} = require("../models/product")
const mongoose = require("mongoose");
const multer = require("multer");
//MIME TYPE LOOKUP
const FILE_TYPE_MAP = {
"image/png" : "png",
"image/jpeg" : "jpeg",
"image/jpg" : "jpg"
}
const storage = multer.diskStorage({
destination: function (req, file, cb) {
const isValid = FILE_TYPE_MAP[file.mimetype]
let uploadError = new Error("invalid Image Format");
if(isValid){
uploadError = null;
}
cb(uploadError, __dirname + '/public/uploads')
},
filename: function (req, file, cb) {//const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9)
const extension = FILE_TYPE_MAP[file.mimetype]
const fileName = file.originalname.split(" ").join("_");
cb(null, Date.now() + "-" + fileName + "." + extension )
}
})
const uploadOptions = multer({ storage: storage })
//SINGLE UPLOADS
router.post(`/`, uploadOptions.single("image"), async (req, res)=>{
let category = await Category.findById(req.body.category);
if(!category) return res.status(400).send("What the fuck? Invalid category");
const file = req.file;
if(!file) return res.status(400).send("What the fuck? I need a file"); //compulsary image file upload similar for the category
const fileName = req.file.filename;
const basePath = `${req.protocol}://${req.get("host")}/public/uploads/`;
const product = new Product({
name: req.body.name,
description: req.body.description,
richDescription: req.body.richDescription,
image: `${basePath}${fileName}`,//"http://localhost:3000/public/upload/image-213213"
//images: req.body.images,
brand: req.body.brand,
price: req.body.price,
category: req.body.category,
countInStock: req.body.countInStock,
rating: req.body.rating,
numReviews: req.body.numReviews,
isFeatured: req.body.isFeatured
})
product1 = await product.save();
if(!product1)
return res.status(500).send("The product cannot be created");
res.send(product1);
})
//MULTIPLE UPLOADS
router.put(
"/gallery-images/:id",
uploadOptions.any("images"),
async (req, res) => {
if(!mongoose.isValidObjectId(req.params.id)){
res.status(400).send("Invalid product id")
}
const files = req.files
let imagePaths = [];
const basePath = `${req.protocol}://${req.get("host")}/public/uploads/`;
if(files){
files.map(file => {
imagePaths.push(`${basePath}${files.fileName}`); })
}
const product = await Product.findByIdAndUpdate(
req.params.id,
{
images: imagePaths
},
{ new : true}
)
if(!product)
return res.status(404).send("The product cannot be updated")
res.send(product);
})
when you were doing the mapping you used file as an object and you put files to get information from filename which is not good. there also it is not fileName but rather filename.
copy and paste this code you should be fine. next time do a console log of the object for more details it can help you in the future
if(files){
files.map(file=>{
//console.log(file)
imagesPaths.push(`${basePath}${file.filename}`);
})
}
and I also give you a bonus for the extension of the original name which does not fade after this :
const fileName = file.originalname.split(" ").join("_");
try that and admire the results
const fileName = file.originalname.replace(/(.png|.jpeg|.jpg)/,'')

Connection time out in mongoose

I'm trying to upload a media file and few strings but I'm getting connection time out.
I want to upload media files like image , video with details of the file to MongoDB.
While the media file is uploaded the details which I enter do not .(description and name of the media file)
the error I'm getting is
[enter image description here][1]
MongooseError: Operation `mediainfos.insertOne()` buffering timed out after 10000ms at Timeout.<anonymous>
the code I'm using is
const conn = mongoose.createConnection(mongoURI, {
useNewUrlParser: true,
useUnifiedTopology: true,
},(err,database)=>{
db = database
})
let gfs;
conn.once("open", () => {
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection("uploads");
});
const storage = new GridFsStorage({
url: mongoURI,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString("hex") + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: "uploads",
};
resolve(fileInfo);
});
});
},
});
const upload = multer({storage});
//Upload Files to DB
app.post('/upload',upload.single('file'),async (req,res)=>{
const fileName = req.file != null ? req.file.filename :null
console.log("Media Name "+req.body.medianame)
const MediaInfo = new mediaInfo({
media_id:fileName,
media_name:req.body.medianame,
media_description:req.body.mediadescription
})
try{
const NewMediaInfo = await MediaInfo.save()
res.send("Works Fine")
}catch(error){
console.log(error)
}
res.redirect('/');
});
MediaInfo is the schema
const Mongoose = require("mongoose");
const Schema = Mongoose.Schema
let mediaInfo = new Schema({
media_id:{
type: String
},
media_name:{
type:String
},
media_description:{
type:String
},
});
module.exports = Mongoose.model('mediaInfos',mediaInfo)
ERROR IMAGE
[1]: https://i.stack.imgur.com/DqMdC.png

Uploading Multiple Images to Cloudinary Using Multer and Express

I have this express backend for adding products to a database, now I have configured it to take product image then name, price, type, and color and it has worked very well so far. But now I am trying to make it so that it can take not one image but up to four but I've been running into issues. The initial code for the single image is as follows
First the config for Cloudinary
const express = require("express");
const cloudinary = require("cloudinary").v2;
const { CloudinaryStorage } = require("multer-storage-cloudinary");
const multer = require("multer");
const verify = require("../routes/verifyToken");
cloudinary.config({
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.API_KEY,
api_secret: process.env.API_SECRET,
});
const storage = new CloudinaryStorage({
cloudinary: cloudinary,
params: {
folder: "Shoes",
format: async (req, file) => {
"jpg", "png";
}, // supports promises as well
public_id: (req, file) => {
console.log(
new Date().toISOString().replace(/:/g, "-") + file.originalname
);
return (
new Date().toISOString().replace(/:/g, "-") + file.originalname
);
},
},
});
const parser = multer({ storage: storage });
Then now the post request to post the shoes(product).
router.post("/post/menshoe", verify,parser.single("shoeImage"), async (req, res) => {
// console.log(req.file);
if (!req.file) return res.send("Please upload a file");
// console.log(req.file); // to see what is returned to you
const image = {};
console.log(req.file)
const shoeUpload = new MenShoe({
shoeImage: req.file.path,
name: req.body.name,
type: req.body.type,
price: req.body.price,
color: req.body.color,
});
console.log(shoeUpload);
try {
const shoe = await shoeUpload.save();
res.json({ msg: "Shoe uploaded", success: true, shoe });
} catch (err) {
console.log(err);
res.json({
msg: "Failed to upload",
success: false,
err,
});
}
}
);
I would like to point out that I've tried to research for a way but each answer that I have encountered is using a completely different way to post images and I am seriously trying to avoid starting to write this from scratch as I have written a lot of code exactly like this. I would really appreciate it if anyone can help me achieve this with just a few tweaks to this code.
Thanks in advance
In your model dir;
const shoeSchema = new mongoose.Schema({
// other properties here
shoeImage: [{
type: String,
required: true // it could be optional
}],
});
module.exports = Shoe = mongoose.model('product', shoeSchema);
Inside your post route,
router.post("/post/menshoe", verify,parser.array("shoeImage", 4), async
(req, res) => {
const { name, type, price, color } = req.body;
try {
let shoeUpload = new MenShoe({
name,
type,
price,
color
});
if (req.files) { // if you are adding multiple files at a go
const imageURIs = []; // array to hold the image urls
const files = req.files; // array of images
for (const file of files) {
const { path } = file;
imageURIs.push(path);
};
shoeUpload['shoeImage'] = imageURIs; // add the urls to object
await shoeUpload.save();
return res.status(201).json({ shoeUpload });
}
if (req.file && req.file.path) {// if only one image uploaded
shoeUpload['shoeImage'] = req.file.path; // add the single
await shoeUpload.save();
return res.status(201).json({ shoeUpload });
};
// you could save here without the image
...
return res.status(400).json({ // in case things don't work out
msg: 'Please upload an image'
});
}catch {
console.error("server error occur", error.message);//only in dev
return res.status(500).send("Server Error Occurred");
}
});

upload multiple files using multerS3 and NodeJs

I've been trying for days but can't seem to figure out where I'm going wrong. My code works for a single file upload in a local folder but can't seem to upload multiple files even if I use upload.array() and req.files in multer and controller function respectively. Whereas when I try to upload a single/multiple files in my s3 bucket nothing gets uploaded at all. Below I'll also provide my model structure in case you need to see it.
::UPDATE::
The files are getting uploaded in s3 bucket but the database can't catch the file path. imagePath is coming blank inside database
route.js:
const ProductController = require('./../controllers/productController');
const appConfig = require("./../config/appConfig");
const multer = require('multer');
const aws = require('aws-sdk');
const multerS3 = require('multer-s3');
const path = require('path');
const s3 = new aws.S3({ accessKeyId: "***", secretAccessKey: "***" });
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'mean-ecom',
metadata: function (req, file, cb) {
cb(null, {fieldName: file.originalname + path.extname(file.fieldname)});
},
key: function (req, file, cb) {
cb(null, Date.now().toString())}})});
let setRouter = (app) => {
let baseUrl = appConfig.apiVersion;
app.post(baseUrl+'/post-product', upload.single('imagePath'), ProductController.postProduct)}
module.exports = {
setRouter: setRouter}
ProductController.postProduct:
const ProductModel = mongoose.model('Product')
let postProduct = (req, res) => {
let newProduct = new ProductModel({
imagePath: req.file && req.file.path})
newProduct.save((err, result) => {
if (err) { console.log('Error at saving new Product :: ProductController', err);
res.send(err);
} else {
console.log('Successfully saved new Product'); res.send(result) }
})}
productModel.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let productSchema = new Schema(
{
imagePath: {
type: String,
default: '' }})
mongoose.model('Product', productSchema);
The first thing you need to do is change the model's imagePath property from a string to an array of strings, so you can store multiple images
const Schema = mongoose.Schema;
let productSchema = new Schema(
{
imagePath: {type: Array}
})
mongoose.model('Product', productSchema)
Then, in the router, you should use upload.any() (upload.array() should work the same way).
Finaly, if you want to store the files locations in the model array, you loop throw the files updating the model.
let postProduct = (req, res) => {
// We create a temporal array with the files locations
let files = [];
// We loop throw the req.files array and store their locations in the temporal files array
for (let i = 0; i < req.files.length; i++) {
let file = req.files[i].location
files.push(file)
}
let newProduct = new ProductModel({
imagePath: files}) //We create the model with the temp array
newProduct.save((err, result) => {
if (err) {
console.log('Error at saving new Product :: ProductController', err);
res.send(err);
}
else{
console.log('Successfully saved new Product');
res.send(result)
}
})

uploading images in mlab

currently i am storing images which are uploaded by node rest server in local directory "/uploads" . this is continuously increasing my repo size .
to avoid this , i want to store image files in mongoDB atlas or mlab just like service.
const express = require("express");
const router = express.Router();
const mongoose = require("mongoose");
const multer = require('multer');
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, './uploads/');
},
filename: function(req, file, cb) {
cb(null, new Date().toISOString() + file.originalname);
}
});
const fileFilter = (req, file, cb) => {
// reject a file
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 * 5
},
fileFilter: fileFilter
});
Please do help me in this. Thanks in advance.
You can achieve this by using a mongoose Schema and the fs core module to encode the image and unlink the file from /uploads.
I would start by creating a Mongoose Schema to set the model of all information you want to store pertaining to your uploaded file.
I'm going to use base64 encoding for this example.
uploadModel.js
const mongoose = require('mongoose');
const fs = require('fs');
const Schema = mongoose.Schema;
mongoose.set('useCreateIndex', true);
let uploadSchema = new Schema({
name: {
type: String,
},
mimetype: {
type: String,
},
size: {
type: Number,
},
base64: {
type: String,
}
})
module.exports = mongoose.model('upload',uploadSchema);
After setting up a model create a function to base64 encode and module.exports that as well.
To encode your file, use fs.readFileSync(path_to_file, encode_type). After the file has been encoded and saved in a variable you can use fs.unlink(path_to_file) to delete the file out of your /uploads folder.
uploadModel.js
module.exports.base64_encode = function(file) {
return new Promise((resolve, reject) => {
if(file == undefined){
reject('no file found');
} else {
let encodedData = fs.readFileSync(file, 'base64');
fs.unlink(file);
resolve(encodedData.toString('base64'));
}
})
}
Now inside your route file require your model.
route.js
const Upload = require('path_to_uploadModel');
router.post('/path_to_upload', upload.single('form_name_of_file'), (req, res) => {
let img = req.file;
let model = new Upload({
name: img.originalname,
size: img.size,
mimetype: img.mimetype,
})
Upload.base64_encode(img.path)
.then((base64) => {
model['base64'] = base64;
model.save((err)=> {
if(err) throw err;
});
}
})
Hope this helps

Resources