I am trying to retrieve a file from mongodb atlas i am using gridfsstream and multer.It keeps giving me this error.
TypeError: this.db.collection is not a function
I can successfully upload but retrieving is not working.What am i missing here
const router = require("express").Router();
const multer = require("multer");
const { mongo, connection } = require("mongoose");
const config = require("../../config/main").db;
const Grid = require("gridfs-stream");
Grid.mongo = mongo;
var gfs = Grid(config);
// set up connection to db for file storage
const storage = require("multer-gridfs-storage")({
url: config,
file: (req, file) => {
return {
filename: file.originalname
};
}
});
// sets file input to single file
const singleUpload = multer({ storage: storage }).single("file");
router.get("/files", (req, res) => {
gfs.files.find().toArray((err, files) => {
if (!files || files.length === 0) {
return res.status(404).json({
message: "Could not find files"
});
}
return res.json(files);
});
I hope still help you, although it passed a few time!! I had the same error when I tried retrieving an image. Below is my code :
MongoClient.connect(config.Info.mongo_database, {}, (err, client) => {
if(err) { new Error("An error has occurred while this file is retrieving: "+ err); }
//throw the error: this.db.collection is not a function
let bucket = new mongo.GridFSBucket(client.db, {
bucketName: bucketName
});
bucket.openDownloadStreamByName(filename).pipe(response);
})
And I solved like this:
MongoClient.connect(config.Info.mongo_database, {}, (err, client) => {
if(err) { new Error("An error has occurred while this file is retrieving: "+ err); }
//Look this, I must to explicit database name, otherwise that error is thrown
let db = client.db('files_repository');
let bucket = new mongo.GridFSBucket(db, {
bucketName: bucketName
});
bucket.openDownloadStreamByName(filename).pipe(response);
})
It works for me now!!
Related
I have a problem when using multer to store a file coming from Front-end using rest api then store the path of that file in mySQL:
here is my code:
route/front_id.js:
var express = require('express');
var multer = require('multer');
var router = express.Router();
var front_idController = require("../app/controllers/front_idController")
router.post("/", function(request, response, next){
var storage = multer.diskStorage({
destination:function(request, file, callback)
{
callback(null, './upload/front_id_image');
},
filename : function(request, file, callback)
{
var temp_file_arr = file.originalname.split(".");
var temp_file_name = temp_file_arr[0];
var temp_file_extension = temp_file_arr[1];
callback(null, temp_file_name + Date.now() + '.' +temp_file_extension);
}
});
var upload = multer({storage:storage}).array('front_id');
upload(request, response,async function(error){
await front_idController (request.files[0].path, request.body.id);
if(error)
{
return response.end('Error Uploading File'+ error);
}
else
{
return response.end('Files is uploaded successfully');
};
});
});
module.exports = router;
controllers/front_idController.js
const { Sequelize, sequelize } = require("../../database/connection");
const Info = require("../models/Info")(sequelize, Sequelize);
const front_idController = async (info, id) => {
const [updatedRows] = await Info.update({
front_id_image: info,
}
,
{
where: { id: id }
}).catch(error => {
console.log(error);
});
if (updatedRows) {
return (`Updated rows: ${updatedRows}`);
} else {
return "User not found";
};
}
module.exports = front_idController
the problem occur when using request.files[0].path as an argument and pass it to front_idController to store it as a path of the file in database
but it getting this Error:
const [updatedRows] = await Info.update({
^
TypeError: (intermediate value) is not iterable
at front_idController (C:\Users\HP\Desktop\Github Project\valenci-backend\app\controllers\front_idController.js:6:27)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async C:\Users\HP\Desktop\Github Project\valenci-backend\routes\front_id.js:34:8
Node.js v18.12.1 [nodemon] app crashed - waiting for file changes
before starting...
according to StefH answer in Async/await not working? #782
the problem was in the bracket in
const [updatedRows] = await Info.update({
I did change it to:
const updatedRows = await Info.update({
I'm trying to send FormData to nodeJs. I appended the values 'id' and 'photo' to the form data. In the front-end, I can clearly see that the FormData is there in the browser's console log, but it is undefined in the nodeJs backend
the backend:
const random = Math.floor(Math.random() * 100000) + 10000;
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './client/public/images');
},
filename: function (req, file, cb) {
cb(null, random.toString() + new Date().getTime() + '.jpg');
},
});
const move = multer({ storage: storage });
const upload = move.single('photo');
router.get('/upload-photo', upload, (req, res) => {
const id = req.body.id;
const photo = req.files['photo'][0].filename;
db.query(
'UPDATE users SET photo = ? WHERE id = ?',
[photo, id],
function (err, rows, fields) {
if (err) throw err;
if (rows.length >= 1) {
rows.map((entry) => {
const user = {};
COLUMNS.forEach((c) => {
user[c] = entry[c];
});
const theUser= {
id: user.id,
photo: user.photo,
};
res.json(theUser);
});
} else {
return res.status(200).json({});
}
}
);
});
The function :
function photoUpload() {
const photoData = new FormData();
photoData.append('id', id);
photoData.append('photo', photoFile);
dispatch(uploadPhoto(photoData));
}
the uploadPhoto action:
export const uploadPhoto = (photoData) => (dispatch) => {
axios
.get(`/api/user/upload-photo`, photoData)
.then((res) => {
dispatch(getPhotos());
})
.catch((err) => {
let message = typeof err.response != 'undefined' ? err.response.data : {};
dispatch({
type: GET_ERRORS,
payload: message,
});
});
};
I don't know if it matters or not, but there is another route like this in a different api file and it works fine. This is basically the same code as that one with the only difference being that the other route uploads multiple files along with multiple req.body data. And that one works perfectly
Instead of:
const photo = req.files['photo'][0].filename;
I had to do:
const photo = req.file.filename; since it was just only one file
Hi i need to upload multiple images at a time on s3.
Currently i am using express-fileupload to upload single image on AWS, and i want to use same approach to make it upload multiple files to s3 and update images array with urls on mongodb.
My schema property:
const ServiceSchema = new mongoose.Schema(
{
photo: [
{
type: String,
default: 'no-photo.jpg',
},
],
});
module.exports = mongoose.model('Service', ServiceSchema);
My Controller:
// #desc Upload photo for service
// #route PUT /api/v1/services/:id/photo
// #access Private
exports.servicePhotoUpload = asyncHandler(async (req, res, next) => {
const service = await Service.findById(req.params.id);
if (!service) {
return next(new ErrorResponse(`Service not found with id of ${req.params.id}`, 404));
}
// Make sure user adding service is business owner
if (service.user.toString() !== req.user.id && req.user.role !== 'admin') {
return next(
new ErrorResponse(
`User ${req.user.id} is not authorized to update this service to business ${service._id}`,
401
)
);
}
// File Upload validation
if (!req.files) {
return next(new ErrorResponse(`Please upload a file.`, 400));
}
const file = req.files.file;
// Make sure it is a valid image file
if (!file.mimetype.startsWith('image')) {
return next(new ErrorResponse(`Please upload a valid image file.`, 400));
}
//Check File Size
if (file.size > process.env.MAX_FILE_UPLOAD) {
return next(
new ErrorResponse(
`Please upload an image less then ${process.env.MAX_FILE_UPLOAD / 1024}KB in size.`,
400
)
);
}
// Create custom filename
file.name = `service-uploads/servicePhoto_${service._id}${path.parse(file.name).ext}`;
uploadToS3({
fileData: req.files.file.data,
fileName: file.name,
})
.then(async (result) => {
console.log('Success Result: ', result);
await Service.findByIdAndUpdate(service._id, { photo: result.Location });
return res
.status(200)
.json({ success: true, message: 'Service photo added successfully', url: result.Location });
})
.catch((err) => {
console.log(err);
return next(new ErrorResponse('Failed to upload file to S3', 500));
});
});
My Utility File to upload File to S3:
const AWS = require('aws-sdk');
const uploadToS3 = (options) => {
// Set the AWS Configuration
AWS.config.update({
accessKeyId: process.env.AWS_S3_ACCESS_KEY,
secretAccessKey: process.env.AWS_S3_SECRET_KEY,
region: 'us-east-2',
});
// Create S3 service object
const s3 = new AWS.S3({ apiVersion: '2006-03-01' });
// Setting up S3 upload parameters
const params = {
Bucket: 'toolbox-uploads',
Key: options.fileName, // File name you want to save as in S3
Body: options.fileData, //
};
// Return S3 uploading function as a promise so return url can be handled properly
return s3.upload(params).promise();
};
module.exports = uploadToS3;
My Router:
const express = require('express');
const {
servicePhotoUpload
} = require('../controllers/service');
const Service = require('../models/Service');
router.route('/:id/photo').put(protect, authorize('publisher', 'business', 'admin'), servicePhotoUpload);
module.exports = router;
This above code is workng 100%.
I am bit confused as there were different approach and none worked for me from google and stack overflow and none of them is getting return url and saving into database.
I want to make separate utility file to upload multiple files to 3 same as i did for single files to use them anywhere. That file should return uploaded urls so i can update my database.
I have tried multer-s3 but no solution works for me.
This approach might be different for you but that is how I was able to resolve the same issue.
First you'll need
Multer
multer-s3
aws-sdk
I made a FileUpload class that handles both single and multi-upload (I also needed to be able to upload pdf and video files) and this is the code in my constructor, note that I also specified the s3-bucket in question from aws.
this.s3 = new AWS.S3({
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_KEY,
Bucket: 'name_of_s3_bucket',
});
I created a method called upload in the class. Code below
upload(path, type) {
let ext = 'jpeg';
const multerFilter = (req, file, cb) => {
if (type === 'image') {
if (file.mimetype.startsWith(this.type)) {
cb(null, true);
} else {
cb(
new AppError(
'Not an Image! Please upload only images',
400
),
false
);
}
} else if (type === 'pdf') {
ext = 'pdf';
const isPdf = file.mimetype.split('/')[1];
if (isPdf.startsWith(this.type)) {
cb(null, true);
} else {
cb(
new AppError('Not a pdf! Please upload only pdf', 400),
false
);
}
}
};
const upload = multer({
storage: multers3({
acl: 'public-read',
s3: this.s3,
bucket: 'name_of_s3_bucket',
metadata: function (req, file, cb) {
cb(null, { fieldName: file.fieldname });
},
key: function (req, file, cb) {
let filename = `user-${
req.user.id
}/${path}/${uuid.v4()}-${Date.now()}.${ext}`;
// eslint-disable-next-line camelcase
const paths_with_sub_folders = [
'auditions',
'biography',
'movies',
];
if (paths_with_sub_folders.includes(path)) {
filename = `user-${req.user.id}/${path}/${
req.params.id
}/${uuid.v4()}-${Date.now()}.${ext}`;
}
cb(null, filename);
},
}),
fileFilter: multerFilter,
limits: {
fileSize: 5000000,
},
});
return upload;
}
To consume the above, I import the class into any controller that I needed an upload feature and called the following.
Side Note : Ignore the paths code (It was just a way to generate unique file name for the files)
const upload = new FileUpload('image').upload('profile-images', 'image');
exports.uploadUserPhoto = upload.array('photos', 10);
I then used the uploadUserPhoto as a middleware before calling the following
exports.addToDB = catchAsync(async (req, res, next) => {
if (!req.files) return next();
req.body.photos = [];
Promise.all(
req.files.map(async (file, i) => {
req.body.photos.push(file.key);
})
);
next();
});
On a high-level overview, this is the flow, First, upload your photos to s3 and get the req.files, then look through that req.files object passing them into an array field on your req object then finally save them on your DB.
NOTE: You must promisify the req.file loop since the task is asynchrnous
My final router looked like this
router
.route('/:id')
.put(uploadUserPhoto, addToDB, updateProfile)
Item.js
Your model can have a field called images thats type array.
const mongoose = require("mongoose");
const ItemSchema = mongoose.Schema({
images: {
type: [],
},
});
module.exports = mongoose.model("Items", ItemSchema);
You map through the array of object and only extract the data you want to store, in this example it is the key which is the unique name given to every image thats uploaded.
route.js
router.post("/", verify, upload.array("image"), async (req, res) => {
const { files } = req;
const images = [];
files.map((file) => {
images.push(file.key);
});
try {
new Item({
images,
}).save();
res.status(200).send({message: "saved images to db"})
}catch(err){
res.status(400).send({message: err})
}
});
Let me know if this does what you wanted
const multer = require('multer');
const storage = multer.memoryStorage();
const upload = multer({ storage });
const csv = require('csvtojson');
public fileUpload = async (req, res): Promise<any> => {
const fileBuffer = req.files[0].buffer.toString();
res.status(200).json({ fileBuffer });
csv({
noheader:true,
output: 'csv',
}).fromString(fileBuffer)
.then((csvRow) => {
csvRow.shift();
this.create(req , res, csvRow);
});
}
//above lines are my code i am getting data in below format i want to
store this data in mongoDB
>[
> [Deepak ,4434444554,454466656,Tata],
> [Naren,8837377373,343443434,Tata]
>]
//this is my create method here i want to match data with schema and store it into database
public create (req, res, object) {
console.log('hi-----------', object);
const { partner_name, mobile_no, alternate_no, vehicle_type } =
object;
try {
const lead = new leadModels({
alternate_no, mobile_no, partner_name, vehicle_type,
});
const newLead = lead.save();
res.status(200).json({ message: 'lead created successfully' });
} catch (err) {
res.status(500).json({ message: appData.internalServerError });
}
}
I've been rewriting my very simple server lately. I have been introducing promises and the final part of my rewrite is implementing my post methods which involves using Multer for uploading images.
This is what I have so far. The GET works perfect, as does the POST when I remove the image upload part of it.
Unfortunately when I introduce Multer I get this error:
TypeError: Cannot convert object to primitive value
at exports.format (util.js:91:18)
at Console.log (console.js:46:37)
at Object.createACategory (/root/api/controllers/controller.js:34:18)
Here is my Route class where I am calling the POST:
'use strict';
module.exports = function(app, gameCategoryList) {
var multer = require('multer');
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, '/var/www/html/uploadsCategoryIcon');
},
filename: function (req, file, callback) {
var originalname = file.originalname;
var extension = originalname.split(".");
callback(null, extension[0] + '-' + Date.now() + '.' + extension[extension.length-1]);
}
});
var upload = multer({ storage: storage });
app.get('/api/gameCategories', (req, res) => {
gameCategoryList.listAllCategories().then(category => res.json(category));
});
app.post('/api/createGameCategory', upload.single('gameCategoryImage'), (req, res) => {
gameCategoryList.createACategory(req.body, req.file).then(category => res.json(category));
});
};
This is what the method looks like in my controller class:
createACategory(body, file) {
var splitPath = file.path.split("html");
var imagePath = 'example.com' + splitPath[1];
var new_category = new Category({
categoryName: body.categoryName,
categoryTag: body.categoryTag,
categoryImageUrl: imagePath
});
return new Promise(function(resolve, reject) {
new_category.save(function(err, category) {
if (err) {
return reject(err)
} else {
return resolve(category)
}
});
}).then((category) => {
if(category)
{
return category
}
else
{
return { success: false }
}
});
}
UPDATE:
On line 34 of controller.js is:
var new_category = new Category({
categoryName: body.categoryName,
categoryTag: body.categoryTag,
categoryImageUrl: imagePath
});
When I remove the image part of this code (categoryImageUrl: imagePath) and don't pass any file or image, this code works fine.