Fetching images from mongoDB using mongoose, nodeJS, express, react - node.js

I have been working off the following guide to upload and fetch images to and from mongoDB and am having issues getting the images to redisplay on the react page after being fetched. I think the issue is with the HTML img src but it could be further up the line so ill walk through everything I have done.
server.js
// -------------------- Mongoose schema -------------------
var imageSchema = new mongoose.Schema({
img:
{
data: Buffer,
contentType: String
}
});
const offeringSchema = new mongoose.Schema({
image: [imageSchema]
});
const Image = mongoose.model("Image", imageSchema)
const Offering = mongoose.model("Offering", offeringSchema);
// -------------------- Set up Multer -------------------
const storage = multer.diskStorage({
destination: './public/uploads/images',
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now() +
path.extname(file.originalname));
}
});
// -------------------- Post Offering Data -------------------
// upload featured offering image
app.post('/admin/OfferingFeatureImage', upload.single('featuredImage'), function (req, res) {
if (req.isAuthenticated()) {
if (req.user.userData[0].admin === true) {
console.log(req.file);
var newImage = new Image();
newImage.img.data = fs.readFileSync(req.file.path)
newImage.img.contentType = 'image/png';
Offering.updateOne({ _id: req.body.offeringID }, {image:[newImage]}, function (err) {
if (err) {
console.log(err)
} else {
res.redirect("/Admin/Offerings");
}
})
} else {
res.send("not-privileged")
}
} else {
res.redirect("/login")
};
});
// -------------------- Get Offering Data -------------------
app.get("/data/allInvestment", function (req, res) {
Offering.find({}, function (err, foundOfferings) {
if (err) {
res.send(err);
} else {
res.send(foundOfferings);
};
});
});
On the client side i am able to get the offering data and see the img data appears to be present
client side data
{props.investmentsData.image[0] !== undefined &&
<img src={"data:" + props.investmentsData.image[0].img.contentType + ";base64," + props.investmentsData.image[0].img.data.toString('base64')} className="card-img-top" alt="..."></img>
}
outcome

Related

Upload video/image file data in node.js and store it in MongoDB

I want to store Image/Video data that user posts into a mongoDB database but it is getting saved as an empty array.
The Multer Configuration:
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,
}).array("media");
module.exports = upload;
The createPost route to upload a new post that can be type of anything like plain-text, Images or videos[Media]:
exports.createPost = async (req, res) => {
try {
const user = await User.findById(req.user._id);
if (!user) return res.status(401).json({ message: "No user found" });
const { media, hashTags, postStatus } = req.body;
const mentions = await User.find(
{ User_name: req.body.mentions },
{ User_name: 1, _id: 0 }
);
if (
req.body.mentions &&
(!Array.isArray(mentions) || mentions.length === 0)
) {
return res.status(404).send("Please enter valid user name");
}
let payLoad = {
userId: req.user._id,
media,
hashTags,
postStatus,
};
if (mentions.length > 0) {
payLoad = { ...payLoad, mentions };
}
let new_post = await new Post(payLoad).save();
return res.status(200).send(new_post);
} catch (error) {
return res.status(500).send(error.message);
}
};
When I pass plain-text in media attribute then It is saved to the database but when I pass image or video from postman form-data then the media is getting saved as an empty array
The API endpoint:
router.post("/createPost", auth, storage, post.createPost);
Can anyone assist me here?

req.body is empty and req.files is undefined when I try to upload a file with multer

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

NodeJS - Upload using Multer with a Promise

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.

Upload images from iOS is too slow

My React-Native iOS front-end can not upload images to my Node.JS (Express + Multer) back-end.
My front-end is React Native Android & iOS. The Android version works fine with no issues, however, uploading images from and iOS device doesn't work most of the time.
Once the upload request is sent, I can see the image file is added in FTP, however, very slowly, like a few KB every second. An image of 500 KB may take 3 minutes or more till the request times out. The file is added to the server partially and I can see change in size with each refresh.
Some [iOS] devices had no issues at all, uploads fast, however, the vast majority of devices are running into this issue.
No connectivity issues. The same host and network work perfectly with Android. Same with some iOS devices.
This is not limited to a specific iOS version or device. However, the devices who had the issue always have it, and those that don't, never have it.
How can I troubleshoot this?
POST request:
router.post('/image', (req, res) => {
console.log('image')
upload(req, res, (error) => {
if (error) {
console.log(error)
return res.send(JSON.stringify({
data: [],
state: 400,
message: 'Invalid file type. Only JPG, PNG or GIF file are allowed.'
}));
} else {
if (req.file == undefined) {
console.log('un')
return res.send(JSON.stringify({
data: [],
state: 400,
message: 'File size too large'
}));
} else {
var CaseID = req.body._case; // || new mongoose.Types.ObjectId(); //for testing
console.log(req.body._case + 'case')
var fullPath = "uploads/images/" + req.file.filename;
console.log(fullPath);
var document = {
_case: CaseID,
path: fullPath
}
var image = new Image(document);
image.save(function(error) {
if (error) {
console.log(error)
return res.send(JSON.stringify({
data: [],
state: 400,
message: 'bad request error'
}));
}
return res.send(JSON.stringify({
data: image,
state: 200,
message: 'success'
}));
});
}
}
});
});
Upload.js:
const multer = require('multer');
const path = require('path');
//image upload module
const storageEngine = multer.diskStorage({
destination: appRoot + '/uploads/images/',
filename: function (req, file, fn) {
fn(null, new Date().getTime().toString() + '-' + file.fieldname + path.extname(file.originalname));
}
});
const upload = multer({
storage: storageEngine,
// limits: {
// fileSize: 1024 * 1024 * 15 // 15 MB
// },
fileFilter: function (req, file, callback) {
validateFile(file, callback);
}
}).single('image');
var validateFile = function (file, cb) {
// allowedFileTypes = /jpeg|jpg|png|gif/;
// const extension = allowedFileTypes.test(path.extname(file.originalname).toLowerCase());
// const mimeType = allowedFileTypes.test(file.mimetype);
// if (extension && mimeType) {
// return cb(null, true);
// } else {
// cb("Invalid file type. Only JPEG, PNG and GIF file are allowed.")
// }
var type = file.mimetype;
var typeArray = type.split("/");
if (typeArray[0] == "image") {
cb(null, true);
}else {
cb(null, false);
}
};
module.exports = upload;
React Native Upload function:
pickImageHandler = () => {
ImagePicker.showImagePicker(this.options1, res => {
if (res.didCancel) {
} else if (res.error) {
} else {
this.setState({upLoadImage:true})
var data = new FormData();
data.append('image', {
uri: res.uri,
name: 'my_photo.jpg',
type: 'image/jpg'
})
data.append('_case',this.state.caseID)
fetch(url+'/image'
, {method:'POST',
body:data
}
)
.then((response) => response.json())
.then((responseJson) =>
{
this.setState(prevState => {
return {
images: prevState.images.concat({
key: responseJson._id,
src: res.uri
})
}
}
)
this.setState({upLoadImage:false})
})
.catch((error) =>
{
alert(error);
});
}
}
)
}
Any suggestions?
Thanks
I saw your answer from UpWork
please try this way,
I'm using API Sauce for API calls
export const addPartRedux = (data) => {
return (dispatch, getState) => {
console.log('addPArtREdux', data);
const values = {
json_email: data.token.username,
json_password: data.token.password,
name: data.text ? data.text : '',
car: data.selected.ID,
model: data.selectedSub.ID,
make_year: data.selectedYear,
type: data.type,
ImportCountry: data.import_image ? data.import_image : '',
FormNumber: data.number ? data.number : '',
do: 'insert'
};
const val = new FormData();
Object.keys(values).map((key) =>
val.append(key, values[key])
);
if (data.imageok) {
val.append('image', {
uri: data.image.uri,
type: data.image.type,
name: data.image.name
});
}
dispatch(loading());
api
.setHeader('Content-Type', 'multipart/form-data;charset=UTF-8');
api
.post('/partRequest-edit-1.html?json=true&ajax_page=true&app=IOS',
val,
{
onUploadProgress: (e) => {
console.log(e);
const prog = e.loaded / e.total;
console.log(prog);
dispatch(progress(prog));
}
})
.then((r) => {
console.log('Response form addPartRedux', r.data);
if (r.ok === true) {
const setting = qs.parse(r.data);
dispatch(addpart(setting));
} else {
dispatch(resetLoading());
dispatch(partstError('Error Loading '));
}
})
.catch(
(e) => {
console.log('submitting form Error ', e);
dispatch(resetLoading());
dispatch(partstError('Try Agin'));
}
);
};
};

How to resize the image before saving to server for Node JS. (multer itself, gm) I am open to any option

I tried to include image resizing before posting. Multer is used to receiving photos. Then, after using input all information including photos.
I would like to reduce the size and quality of image before they post. However, it's doesn't work. Anyone can giving suggestion?
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'photo')
},
filename: function (req, file, cb) {
cb(null, 'car-' + Date.now() + '.png')
},
})
const upload = multer({ storage: storage })
const gm = require('gm');
module.exports = (app,passport) => {
app.post('/sell', isLoggedIn, upload.any(), (req,res,next) => {
gm(req.files)
.resize(250, 250)
.gravity('Center')
.extent(width, height)
.noProfile()
.write('/photo/' + req.file.fieldname + '-' + Date.now())
async.waterfall([
function(callback) {
var newCar = new Car();
newCar.owner = req.user._id;
newCar.make = req.body.make;
newCar.model = req.body.model;
newCar.year = req.body.year;
newCar.mileage = req.body.mileage;
newCar.price = req.body.price;
newCar.detail = req.body.detail;
newCar.locationProvince = req.body.locationProvince;
newCar.locationDistrict = req.body.locationDistrict;
//newCar.image = req.files;
newCar.save((err) => {
callback(err, newCar);
});
},
function (newCar, callback) {
User.update (
{
_id: req.user._id
},{
$push: {cars: newCar._id }
}, function (err,count) {
req.flash('success', 'success')
res.redirect('/')
}
)
}
]);
});
}
Firstly, please specifies error or something more about your problem. I think you need to console.log -> res.files, it could be an array. Also, check your path in write if it's correct. And the last one, probably you don't add callback function -> write(path, cb).
I can solve it now. But, I don't know how to save the image which have been resized to mongoose.
app.post('/sell', isLoggedIn, upload.any(), (req, res, next) => {
async.waterfall([
function(callback) {
console.log('files', req.files)
if (req.files.length > 0) {
req.files.map(function(file) {
var x = gm(file.path)
.resize(800, 640)
.gravity('Center')
//.extent(250, 250)
.noProfile()
.quality(80)
.write('./photo/resized/' + file.filename +'-800x640', function(err) {
if (err) {
console.log('error : ', err)
}
console.log(file.filename + ' resized!')
});
})
//console.log(req.files.path)
//console.log(req.files)
var newCar = new Car();
newCar.owner = req.user._id;
newCar.make = req.body.make;
newCar.model = req.body.model;
newCar.year = req.body.year;
newCar.mileage = req.body.mileage;
newCar.price = req.body.price;
newCar.detail = req.body.detail;
newCar.locationProvince = req.body.locationProvince;
newCar.locationDistrict = req.body.locationDistrict;
newCar.image = req.files;
newCar.image_resized = x;
newCar.save((err) => {
callback(err, newCar);
});
}
},

Resources