Could not decode base64 error when upload images - node.js

I am building Ecommerce in the MERN stack. Products can have multiple images. I use cloudinary to upload images. When I upload more pictures (slightly larger pictures) I get an error Could not decode base64 error.
This is my createProduct controller:
export const createProduct = async (req, res) => {
const { name, description, price, color, size, quantity, category } =
req.body;
let images = [];
if (typeof req.body.images === 'string') {
images.push(req.body.images);
} else {
images = req.body.images;
}
const imagesLinks = [];
for (let i = 0; i < images.length; i++) {
const result = await cloudinary.v2.uploader.upload(images[i], {
folder: 'products',
});
imagesLinks.push({
public_id: result.public_id,
url: result.secure_url,
});
}
req.body.images = imagesLinks;
const newProduct = new Product({
name,
description,
quantity,
images: imagesLinks,
price,
color: JSON.parse(color),
size: JSON.parse(size),
category,
user: req.user.userId,
});
const product = await newProduct.save();
res.status(StatusCodes.CREATED).json({ product });
};
When I upload smaller images, I don't get an error and everything works great. Is there a solution to this problem, something like reducing the image size when uploading, changing it to webp format so that it takes up less space, or something similar?

Related

retrieve image from mongodb and display on client side

Now I'm using express, node.js, and mongodb. I just saw that the images can be stored to mongodb with multer and grid fs storage and it works.
enter image description here
enter image description here
And I need to get back to client side. I guess the image can be converted from that chunk binary to image but I really sure how to do so. My ultimate purpose is to display menu with name, price, and picture from mongodb which I uploaded.
Does anyone know how to retrieve it and send image file from controller to boundary class?
Additional resources:
//this is entity class which is for obtaining information about image file
static async getImages(menu) {
try {
let filter = Object.values(menu.image)
const files = await this.files.find({ filename: { $in: filter } }).toArray()
let fileInfos = []
for (const file of files) {
let chunk = await this.chunks.find({ files_id: file._id }).toArray()
console.log(chunk.data)
fileInfos.push(chunk.data)
}
return fileInfos
} catch (err) {
console.log(`Unable to get files: ${err.message}`)
}
}
** so chunk object contains this**
{
_id: new ObjectId("627a28cda6d7935899174cd4"),
files_id: new ObjectId("627a28cda6d7935899174cd3"),
n: 0,
data: new Binary(Buffer.from("89504e470d0a1a0a0000000d49484452000000180000001808020000006f15aaaf0000000674524e530000000000006ea607910000009449444154789cad944b12c0200843a5e3fdaf9c2e3a636d093f95a586f004b5b5e30100c0b2f8daac6d1a25a144e4b74288325e5a23d6b6aea965b3e643e4243b2cc428f472908f35bb572dace8d4652e485bab83f4c84a0030b6347e3cb5cc28dbb84721ff23704c17a7661ad1ee96dc5f22ff5061f458e29621447e4ec8557ba585a99152b97bb4f5d5d68c92532b10f967bc015ce051246ff76d8b0000000049454e44ae426082", "hex"), 0)
}
//this is controller class
static async apiViewMenu(_req, res) {
try {
let menus = await MenusDAO.getAllMenus()
for (const menu of menus) {
menu.images = await ImagesDAO.getImages(menu)
}
//return menus list
res.json(menus)
} catch (err) {
res.status(400).json({ error: err.message })
}
}
I did not handle converting this buffer data to image because I do not know...

How to display base64 string as image in email, using AWS SES

I am trying to show an image inside my e-mail. But that image is not getting displayed. I am using base64 string, which I am fetching from S3 bucket.
I am able to get email in inbox, but only thing image is not working when passing url, if directly using base64 hard coded string in html its working.
I need to fetch image from s3 and that image should be inline with email.
"use strict";
const fs = require("fs");
const path = require("path")
const Handlebars = require('handlebars');
const {SESClient, sendEmailCommand} = require("#aws-sdk/client-ses");
const {S3Client, GetObjectCommand} = require("#aws-sdk/client-s3");
let S3=null, SES=null;
const streamToBuffer = async(stream) =>{
return new Promise((resolve, reject) =>{
const chunks = [];
stream.on("data", (chunk) =>{chunks.push(chunk)});
stream.on("error", reject);
stream.on("end", () =>{resolve(Buffer.conact(chunks))});
})
}
export.handler = async(event) =>{
if(S3 === null){
S3 = new S3Client ({region: MY_REGION})
}
if(SES === null){
SES = new SESClient ({region: MY_REGION})
}
try{
let deatils = event.detail.fullDocument;
let imageKey = `${deatils.dir}/myimage.png`;
let imageFileFromS3 = await S3.send(
new GetObjectCommand({
Bucket: MY_BUCKET_NAME, key: imageKey
}))
let imageFileBuffer = await streamToBuffer(imageFileFromS3.Body)
let bufferToBase64 = imageFileBuffer.toString("base64");
const emailSubject = "Hey!! Test mail with image";
const emailData = {
Name: "Email Tester"
ImageSrc: `data:image/png;base64, ${bufferToBase64}`
}
let htmlTemplate = Handlebars.complie(fs.readFileSync(path.join(__dirname, 'templateSrc', email.html)).toString())
let textTemplate = Handlebars.complie(fs.readFileSync(path.join(__dirname, 'templateSrc', email.txt)).toString())
let emailResult = await SES.send( new SendEmailCommand({
Source: "Source_test#email.com", //dummy email for now
Destination :{
ToAddress: ["to_test#email.com"] // dummy address
},
Message: {
Subject: {
Charset: 'UTF-8',
Data: emailSubject
},
Body: {
Text: {
Charset: 'UTF-8',
Data: textTemplate(emailData)
},
Html:{
Charset: 'UTF-8',
Data: htmlTemplate(emailData)
}
}
}
}))
return emailResult
}catch(error){
console.log(error)
}
}
email.txt
Dear {{Name}}
Thanks for asking images on email.
Please find your requested images below
Face image
Bus image
-----Thanks
Email.html
<h1>Dear {{Name}}</h1>
<p>Thanks for asking images on email.</p>
<p>Please find your requested image below</p>
<p>face Image</p>
<img src={{ImageSrc}} />
<p>Bus Image</p>
<img src="data:image/gif;base64,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">
//This image is working
<p>-------Thanks</p>
I have just resolved this issue...
So I thought, about posting answer for others help.
The root cause of this was- large size of my buffer response form S3, and email only supports 128MB data, as I found in cloud watch logs ( I can comment about AWS SES only, not sure about other email clients)
So the ultimate solution for my problem is just to resize the buffer response, which we are getting from S2.
So I have used sharp https://www.npmjs.com/package/sharp
And add these line in index.js
//Here I will resize the image
const resizedImageFileBuffer =
await sharp(imageFileBuffer)
.resize ({
width:200,
height:200,
fit: 'contain'
})
.toFormat('png')
.png({
quality:100,
compressionLevel: 6
})
.toBuffer()
//Now we will convert resized buffer to base64
let bufferToBase64 =
resizedImageFileBuffer.toString("base64");

POST request with formidable returning 500 - Node.js

I need to save data and file as a new project to my Mongo. For this I am using formidable.
My POST method looks like this:
exports.create = async (req, res) => {
let form = new formidable.IncomingForm();
form.keepExtensions = true;
form.parse(req, (err, fields, files) => {
if (err) {
return res
.status(400)
.json({ errors: [{ msg: 'Image could not be uploaded' }] });
}
const {
title,
description,
photo,
tags,
git,
demo,
projectType,
} = fields;
//get links object
const projectFields = {};
projectFields.creator = req.user._id;
if (title) projectFields.title = title;
if (title) projectFields.description = description;
if (photo) projectFields.photo = photo;
if (projectType) projectFields.projectType = projectType;
if (tags) {
projectFields.tags = tags.split(',').map((tag) => tag.trim());
}
//get links object
projectFields.links = {};
if (git) projectFields.links.git = git;
if (demo) projectFields.links.demo = demo;
//1kb = 1000
//1mb = 1000000kb
//name 'photo' mus match client side. use photo
if (files.photo) {
if (files.photo.size > 1000000) {
return res.status(400).json({
errors: [{ msg: 'Image could not be uploaded. File to big.' }],
});
}
//this relates to data in schema product
project.photo.data = fs.readFileSync(files.photo.path);
project.photo.contentType = files.photo.type;
}
});
I want to use async/await so I am using try{}catch(err){} for my project.save(). I am initializing all my fields where I have also nested links. Unfortunately this is not working as I thought it will work. Right now my POST is returning 500. I am sitting on this and right now I am at the point that this can get a bit messy and not even close to any solution.

Adding image dynamically in public folder in reactjs

I am developing an face detection application,for that I need to collect the users image for reference to detect them later.i have successfully uploaded the image in MySQL databse.now I need upload the image in public folder in react to detect the image in camera.i stuck in uploading image in react public folder.help me out get rid of this problem..
This is the React code where image to be detected in the imgUrl variable
detect = async () => {
const videoTag = document.getElementById("videoTag");
const canvas = document.getElementById("myCanvas");
const displaySize = { width: videoTag.width, height: videoTag.height };
faceapi.matchDimensions(canvas, displaySize);
//setInterval starts here for continuous detection
time = setInterval(async () => {
let fullFaceDescriptions = await faceapi
.detectAllFaces(videoTag)
.withFaceLandmarks()
.withFaceExpressions()
.withFaceDescriptors();
const value = fullFaceDescriptions.length;
this.setState({ detection: value });
fullFaceDescriptions = faceapi.resizeResults(
fullFaceDescriptions,
displaySize
);
canvas.getContext("2d").clearRect(0, 0, canvas.width, canvas.height);
//Label Images
var dummy = ["praveen", "vikranth", "Gokul", "Rahul"];
const labels = nameArray1;
// const labels = ["praveen", "vikranth", "Gokul", "Rahul"];
if (no_of_times <= 0) {
if (no_of_times === 0) {
labeledFaceDescriptors = await Promise.all(
labels.map(async (label) => {
// fetch image data from urls and convert blob to HTMLImage element
const imgUrl = `/img/${label}.png`; // for testing purpose
// const imgUrl = testImage;
const img = await faceapi.fetchImage(imgUrl);
const fullFaceDescription = await faceapi
.detectSingleFace(img)
.withFaceLandmarks()
.withFaceExpressions()
.withFaceDescriptor();
if (!fullFaceDescription) {
throw new Error(`no faces detected for ${label}`);
}
const faceDescriptors = [fullFaceDescription.descriptor];
return new faceapi.LabeledFaceDescriptors(label, faceDescriptors);
})
);
// console.log(no_of_times);
}
}
const maxDescriptorDistance = 0.7;
no_of_times++;
const faceMatcher = new faceapi.FaceMatcher(
labeledFaceDescriptors,
maxDescriptorDistance
);
const results = fullFaceDescriptions.map((fd) =>
faceMatcher.findBestMatch(fd.descriptor)
);
result = [];
results.forEach((bestMatch, i) => {
const box = fullFaceDescriptions[i].detection.box;
// console.log(box)
const text = bestMatch.toString(); //this for basMatch name detection
var str = "";
//This is for removing names confidence to map value without duplicate
var val = text.replace(/[0-9]/g, "");
for (let i of val) {
if (i !== " ") {
str += i;
} else {
break;
}
}
if (result.includes(str) === false) result.push(str);
const drawBox = new faceapi.draw.DrawBox(box, { label: text });
drawBox.draw(canvas);
faceapi.draw.drawFaceExpressions(canvas, fullFaceDescriptions, 0.85);
});
for (let i = 0; i < fullFaceDescriptions.length; i++) {
const result1 = fullFaceDescriptions[i].expressions.asSortedArray()[i];
// console.log(result[i]);
// console.log(result1.expression);
this.test(result[i], result1.expression);
}
}, 100);
In the above code i am manually putting image in public folder,this need to be done dynamically when the user uploads image.
this is place i get the images in base64 from nodejs
axios.get("/image").then((res) => {
testImage = res.data;
// console.log("from image" + res.data);
imgback = <img src={`data:image/jpeg;base64,${res.data}`} />;
});
This is nodejs code for the get request from reactjs
app.get("/image", (req, res) => {
connection.query("SELECT * FROM images", (error, row, fields) => {
if (!!error) {
console.log("Error in the query");
} else {
console.log("successful query");
var buffer = new Buffer(row[0].image, "binary");
var bufferBase64 = buffer.toString("base64");
res.send(bufferBase64);
}
});
});
my goal is, in the imgUrl variable in react code i need to specify the image folder for that i need to dynamically add image in folder.
Or is there is any other way to directly give image array in the imgUrl variable.please help me to sort out this problem.

multer and node/express file upload not working with Angular

I'm trying to do single file upload using multer. Although I'm able to upload a file manually in the relevant folder using tools like postman for testing the express route, I cannot upload it using Angular frontend. I created a folder called uploads in the node backend folder where the files are supposed to get uploaded. Also I need to upload the file within a form and pass it to a api where it should take the file along with other parameters also. But unfortunately, it is returning status 500 with Internal Server Error on the browser console while on my node terminal it is returning Cannot read property 'path' of undefined.
My node backend code which is working fine is below:
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 upload = multer({storage: storage})
let baseUrl = appConfig.apiVersion+'/blogs';
app.post(baseUrl+'/create', upload.single('imagePath'), (req, res) => {
var today = time.now()
let blogId = shortid.generate()
let newBlog = new BlogModel({
blogId: blogId,
title: req.body.title,
description: req.body.description,
bodyHtml: req.body.blogBody,
isPublished: true,
category: req.body.category,
author: req.body.fullName,
created: today,
lastModified: today,
imagePath: req.file.path //node console is pointing towards this point
}) // end new blog model
let tags = (req.body.tags != undefined && req.body.tags != null && req.body.tags != '') ? req.body.tags.split(',') : []
newBlog.tags = tags
newBlog.save((err, result) => {
if (err) {
console.log(err)
res.send(err)
} else {
res.send(result)
}
}) // end new blog save
});
Below is my Angular Component code which is not working:
selectImage(event) {
if(event.target.files.length > 0){
const file = event.target.files[0];
this.images = file;
}
}
public createBlog(): any {
const formData = new FormData();
const form = formData.append('imagePath', this.images);
let blogData = {
title: this.blogTitle,
description: this.blogDescription,
blogBody: this.blogBodyHtml,
category: this.blogCategory,
imagePath: form
} //end blogData
console.log(blogData);
this.blogHttpService.createBlog(blogData).subscribe(
data => {
console.log(data);
this.toastr.successToastr('Blog Posted Susseccfully!', 'Success!');
setTimeout(() =>{
this.router.navigate(['blog', data.blogId]);
}, 1000)
},
error => {
console.log(error);
console.log(error.errorMessage);
this.toastr.errorToastr('This is not good!', 'Oops!');
})
}
Angular Service code
public createBlog(blogData): any {
let myResponse = this._http.post(this.baseUrl + '/create', blogData);
return myResponse;
}
Frontend HTML Code:
<div>
<input type="file" name="imagePath" (change)="selectImage($event)" />
</div>
It seems like you created a formData object, but you are not actually doing anything with it. As you can see here, you are building up an object and sending it along with your request, but it does not include your formData
let blogData = {
title: this.blogTitle,
description: this.blogDescription,
blogBody: this.blogBodyHtml,
category: this.blogCategory,
imagePath: this.imagePath
} //end blogData
console.log(blogData);
this.blogHttpService.createBlog(blogData).subscribe(
Not entirely sure what the exact syntax would be in your case, but here you can see some sample code I have in a project of mine which will hopefully give you an idea.
changeHandler(e) {
const fd = new FormData();
fd.append('sample', e.target.files[0]);
axios.post('/save-image', fd).then(i => {
this.setState({img: i.data.filename});
});
}
As you can see, the formData is what I am actually sending to the server.

Resources