I want to add some company details to mongo DB, and the details include a company logo. So I want to upload the picture to Cloudinary and then save the URL in Mongo DB with the other details.
But my code doesn't seem to work. When I fill the form and click on submit, the image gets uploaded to Cloudinary but it does not get saved in the Database.
To store the image
const [ companyLogo, setCompanyLogo] = useState("");
const [ companyLogoURL, setCompanyLogoURL] = useState("");
Function to execute on submit
const handleCompanySubmit = (evt) => {
evt.preventDefault();
const data = new FormData()
data.append("file", companyLogo)
data.append("upload_preset", "Sprint")
data.append("cloud_name", "sprint-ccp")
fetch("https://api.cloudinary.com/v1_1/sprint-ccp/image/upload",{
method:"post",
body:data
})
.then(res => res.json())
.then(data => {
setCompanyLogoURL(data.url)
})
.catch(err => {
console.log(err)
})
//check for empty fields
if (
isEmpty(companyName) ||
isEmpty(companyAddress) ||
isEmpty(companyRegNumber) ||
isEmpty(companyContactNumber)
) {
setCompanyErrorMsg("Please Fill All The Fields");
}else {
let formData = new FormData();
formData.append('companyName', companyName);
formData.append('companyAddress', companyAddress);
formData.append('companyRegNumber', companyRegNumber);
formData.append('companyContactNumber', companyContactNumber);
formData.append('companyLogo', companyLogoURL);
setCompanyLoading(true);
addCompany(formData)
.then((response) => {
setCompanyLoading(false);
setCompanySuccessMsg(response.data.successMsg)
setCompanyData({
companyName: "",
companyAddress: "",
companyRegNumber: "",
companyContactNumber: ""
});
})
.catch((err) => {
setCompanyLoading(false);
setCompanyErrorMsg(err.response.data.errorMsg)
})
}
};
const handleCompanyLogo = (evt) => {
setCompanyLogo(evt.target.files[0])
};
frontend view
<form className="register-form" onSubmit={handleCompanySubmit} noValidate>
<label className="text-secondary">Company Logo :</label>
<input type="file" className="form-control" onChange={handleCompanyLogo}/>
//remaining input fields
<button className="btn btn-info submitButton" >Submit</button>
</form>
api for adding company
export const addCompany = async (data) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
const response = await axios.post(
"http://localhost:5000/api/auth/clients/company",
data,
config
);
return response;
};
controller in backend
exports.addNewCompany = async(req,res)=>{
const {
companyName,
companyAddress,
companyRegNumber,
companyContactNumber,
companyLogo
} = req.body;
const company = await Company.findOne({ companyName });
if (company) {
return res.status(400).json({
errorMsg: `${req.body.companyName} already exists`,
});
}
try{
const newCompany = new Company();
newCompany.companyName = companyName;
newCompany.companyAddress = companyAddress;
newCompany.companyRegNumber = companyRegNumber;
newCompany.companyContactNumber = companyContactNumber;
newCompany.companyLogo = companyLogo;
await newCompany.save();
res.json({
successMsg: `${req.body.companyName} Company Added Successfully`
});
} catch (err) {
console.log("clientsController error - Add Company ", err);
res.status(500).json({
errorMsg: "Server Error. Please Try again",
});
}
};
The error i get in the console is this
clientsController error - Add Company Error: Company validation failed: companyLogo: Path companyLogo is required.
at ValidationError.inspect
(C:\CCP\sd08_2021\Backend\node_modules\mongoose\lib\error\validation.js:47:26)
Can you please help me out ?
I think that your error is caused by a more trivial problem :
When you send the POST request with fetch, you don't actually wait for its completion (it's a promise), so the code in the if ... else {...} statement is executed before the termination of the fetch() !
setCompanyLogoURL(data.url) has not been called yet, so formData.append('companyLogo', companyLogoURL); set a blank string instead of the value returned by the call to the Cloudinary API.
The solution would be to make handleCompanySubmit async, and to await for the fetch() promise completion.
Related
I make a crud with products
I send an http request to the /api/deleteProduct route with the product id to retrieve it on the server side and delete the product by its id
To create a product it works only the delete does not work
pages/newProduct.js :
useEffect(() => {
async function fetchData() {
const res = await axios.get('/api/products');
setProducts(res.data);
}
fetchData();
}, []);
const handleSubmit = async (event) => {
event.preventDefault();
const formData = new FormData();
formData.append('picture', picture);
formData.append('name', name);
formData.append('price', price);
formData.append('category', category);
formData.append('description', description);
try {
const res = await axios.post('/api/createProduct', formData);
console.log(res.data);
} catch (error) {
console.log(error);
}
};
const handleDelete = async (id) => {
try {
await axios.delete(`/api/deleteProduct?id=${id}`);
setProducts(products.filter(product => product._id !== id));
} catch (error) {
console.log(error);
}
};
api/deleteProduct.js :
import Product from '../../models/Products';
import { initMongoose } from '../../lib/mongoose';
initMongoose();
export const handleDelete = async (req, res) => {
if (req.method === 'DELETE'){
try {
const { id } = req.params
const product = await Product.findByIdAndRemove(id);
if (!product) {
return res.status(404).json({ message: 'Product not found' });
}
return res.status(200).json({ message: 'Product deleted successfully' });
} catch (error) {
console.log(error);
return res.status(500).json({ message: 'Database error' });
}
}};
I have a 500 error but no error in the server side console and the console.log is not showing like the file was not read
Based on the code you've shared, it seems that the problem may be with the way that the delete request is being handled on the frontend. Specifically, in this line:
await axios.delete("/api/deleteProduct", { params: { id } });
The delete request is supposed to receive the id of the product that should be deleted as a query parameter, but it is being passed as a request body.
Instead of passing it as a parameter, you should pass it as a query parameter by changing it to
await axios.delete(`/api/deleteProduct?id=${id}`);
Also, in your api/deleteProduct.js, you should change the following line:
const { id } = req.query;
to
const { id } = req.params;
Also, you should make sure that the server is running and that the api endpoint '/api/deleteProduct' is accessible and handling the request correctly.
For the last, make sure that the product model is imported and initialized correctly and the database connection is established.
Hope that it solves your problem or, at least, helps :))
I succeeded, I put this (server side):
const { id } = req. query;
and (client side):
await axios.delete(/api/deleteProduct?id=${id});
and I exported my function like this:
export default async function handleDelete(req, res) {
I'm trying to upload images to my database using the rich-markdown-editor, however, everytime I console.log() my uploads/uploadObject route, nothing gets retrieved; I'm using express-fileupload and it has always worked before, however, since I decided to use said text-editor, everything started to go downhill.
Here it is the code I use for my uploading of files:
exports.uploadObject = asyncHandler(async (req, res, next) => {
console.log('Req body:', req.body)
console.log('Req files:', req.files)
console.log('Req query:', req.query)
const file = await cloudinary.uploader.upload(req.files)
res.status(201).json({
success: true,
data: {
insecure_url: file.url,
secure_url: file.secure_url,
},
})
})
I have put, several methods to see if the file is actually received on my backend but no success so far. Yes, I tried to send it via query, ?uploadedFile=${file}....did not work either.
Here it is the code from my textEditor component
<Editor
name={name}
id={id}
dark={true}
defaultValue={text}
uploadImage={async (e) => {
try {
const file = await resizeFile(e) // returns base64 string
const res = await axios.get(`/uploads/uploadObject`, file)
console.log('Upload action', res)
} catch (err) {
// const error = err.response.data.message;
const error = err?.response?.data?.error?.errors
const errors = err?.response?.data?.errors
if (error) {
// dispatch(setAlert(error, 'danger'));
error &&
Object.entries(error).map(([, value]) => toast.error(value.message))
}
if (errors) {
errors.forEach((error) => toast.error(error.msg))
}
toast.error(err?.response?.statusText)
return {
msg: err?.response?.statusText,
status: err?.response?.status,
}
}
}}
onChange={(e) => {
setBlogData({
...blogData,
text: e(),
})
}}
readOnly={isReadOnly}
/>
If you guys could give me an insight into what the problem might be, I would be very thankful.
I have never had this happen before and am not sure why it's happening.
I have a component written to display PDF files in an iframe as part of a larger application. I am retrieving a BLOB stream from the server and attempting to create a URL for it to display in the iframe but it keeps giving me a cross-origin error, which I thought would not be possible since it is creating the URL out of data.
Here is my entire component:
import React, { useState, useEffect } from 'react'
import IFrameComponent from '../Elements/IFrameComponent';
const PDFPages = (props) => {
let [file, setFile] = useState(null)
let [notFound, show404]=useState(false)
useEffect(() => {
let id=props.site?.componentFile;
fetch(`${process.env.REACT_APP_HOST}/documents/GetPDF`,
{
method: 'POST'
, headers: {
'Content-Type': 'application/json'
}
, credentials: 'include'
, body: JSON.stringify({file:id})
})
.then(async response => {
let blob;
try{
blob=await response.blob(); // <--- this functions correctly
}
catch(ex){
let b64=await response.json()
blob=Buffer.from(b64.fileData,'base64')
}
//Create a Blob from the PDF Stream
//Build a URL from the file
const str=`data:application/pdf;base64,${b64.fileData}`
const url=URL.createObjectURL(blob) //<--- ERROR IS THROWN HERE
setFile(url);
})
.catch(error => {
show404(true)
});
}, []);
if(!notFound){
return <IFrameComponent src={file} title=''>
Please enable iFrames in your browser for this page to function correctly
</IFrameComponent>
}
else {
return (
<>
<h3> File {file} could not be found on server</h3>
</>
)
}
}
export default PDFPages;
For completeness here is the GetPDF function from the server which is sending the file.
router.post('/GetPDF', async (req, res, next) => {
const props = req.body;
let fileName = props.file;
try {
fileName = fileName.replace(/%20/g, " ");
let options = {};
if (props.base64) options.encoding = 'base64'
let data = await dataQuery.loadFile(`./data/documentation/${fileName}`, options);
if (!props.base64) {
res.attachment = "filename=" + fileName
res.contentType = 'application/pdf'
res.send(data);
}
else{
res.send({fileData:data, fileName: fileName});
}
}
catch (ex) {
res.send({ error: true })
}
});
I have done very little work in node sending files but am positive my client code is good. Where am I going wrong here?
The problem was that I was trying to be too fancy sending a BLOB or Base64 data. After investigation I rewrote
router.post('/GetPDF', async (req, res, next) => {
const props = req.body;
let fileName = props.file;
try {
fileName = fileName.replace(/%20/g, " ");
let options = {};
if (props.base64) options.encoding = 'base64'
let data = await dataQuery.loadFile(`./data/documentation/${fileName}`, options);
if (!props.base64) {
res.attachment = "filename=" + fileName
res.contentType = 'application/pdf'
res.send(data);
}
else{
res.send({fileData:data, fileName: fileName});
}
}
catch (ex) {
res.send({ error: true })
}
});
on the server to
router.get('/GetPDF/:fileName', async (req, res, next) => {
let fileName = req.params.fileName
fileName = `./data/documentation/${fileName.replace(/%20/g, " ")}`;
try {
let data = await dataQuery.loadFile(fileName);
res.contentType("application/pdf");
res.send(data);
}
catch (ex) {
res.send({ error: true })
}
});
Then calling it from the client using
const url = `${process.env.REACT_APP_HOST}/documents/GetPDF/${props.site.componentFile}`
as the iFrame src sends the PDF properly as expected.
This same method also solved another problem with HTML pages sent from the server not functioning correctly.
When I send data using postman the Title,description and image I am sending goes through.
This is my post array:
router.post('/', uploadS3.array('meme',3),(req, res, next)=>{
// res.json(req.file.location)
console.log(`req.files =================${req.headers}`);
// console.log("req.file.location===",req.file.location, "req.body===",req.body);
const locationURL = req.files.map(item=>item.location);
//Send image alongside with other form fields
postModel.create({...req.body,image:locationURL}, (error, returnedDocuments) => {
if (error) return next(error);
res.json(returnedDocuments);
})
})
my postman:
When I try to send data using my front end, it does not go to the backend properly, the response I recieve is nothing if I do not include an image, and the title and description is not being passed.... notice that my postModel expects the title and description to be inside of req.body
postModel.create({...req.body,image:locationURL}
I am sending the data by appending it to the FormData() object
Just to be clear, setFiles is the setter for file const [file, setFiles] = useState({ selectedFile: null, loaded: 0 });
const onChangeFile = event => {
if (maxSelectFile(event) && checkMimeType(event) && checkFileSize(event)) {
setFiles({
selectedFile: event.target.files
});
}
console.log(event.target.files)
}
const submitFormHandle = event => {
const data = new FormData();
for (let i = 0; i < file.selectedFile.length; i++) {
data.append("meme", file.selectedFile[i]);
}
data.append("title", title);
data.append("description", description);
axios
.post("http://localhost:3200/api/posts/", data)
.then(res => {
toast.success('upload success')
})
.catch(err => {
toast.error('upload fail')
})
}
You can post axios data by using FormData using below approach :
For the field try using set on FormData object and for image use append like this.
Change your code to:
data.set("title", title);
data.set("description", description);
Kindly let me know if this doesn't work.
I know this has been asked before, but none of the solutions are working for me. First I tried to solve this using axios, but reading about it there seem to be a bug that won't allow me to use it for uploading files. So I'm stuck with fetch.
This is my uploading function:
export async function postStudyPlan(plan, headers) {
const options = {
method: "POST",
body: plan
}
return fetch(`${HOST}:${PORT}/study-plans/add`, options)
.then(res => {return res.json()})
.catch(err => console.log(err));
}
This is how I call it:
onStudyPlanUpload(files) {
const file = files[0];
let formData = new FormData();
formData.append("pdf", file);
formData.append("comments", "A really lit study plan!");
formData.append("approved", true);
formData.append("uploaded_by", "Name");
formData.append("date_uploaded", "2012-02-1");
formData.append("university", "australian_national_university");
let plan = {
"pdf": file,
"comments": "A really lit study plan!",
"approved": true,
"uploaded_by": "Name",
"date_uploaded": Date.now(),
"university": "australian_national_university"
}
postStudyPlan(formData)
.then(res => console.log(res))
.catch(err => console.log(err))
}
I know that file is in fact a file. Whenever I change "pdf" to a normal string, everything works fine. But when I use a File object, I recieve nothing to my backend, just an empty object. What am I missing here? I feel like my solution is basically identical to every other solution I've found online.
Edit:
Also tried using FormData and adding headers: {"Content-Type": "application/x-www-form-urlencoded"} to options. Same result.
Edit 2
I'm beginning to think my backend might be the problem! This is my backend, and I'm actually getting some outputs for the "data" event. Not sure how to process it...
router.route("/add").post((req, res) => {
req.on("data", function(data) {
console.log("got data: " + data.length);
console.log("the Data: ?" )
// let t = new Test(data);
// t.save()
// .then(res => console.log(res))
})
req.on("end", function(d) {
console.log("ending!");
})
req.on("error", function(e){
console.log("ERROR: " + e);
})
});
You should use FormData with 'Content-Type': 'application/x-www-form-urlencoded' as fetch header.
I want you to try a simple approach. Instead of appending the file into FormData, create an instance of an actual form.
onStudyPlanUpload = (event) => {
event.preventDefault();
const formData = new FormData(event.target);
postStudyPlan(formData)
.then(res => console.log(res))
.catch(err => console.log(err))
}
HTML
<form onSubmit={this.onStudyPlanUpload} encType="multipart/form-data" ref={el => this.form = el}>
<input type="file" name="pdf" onChange={() => { this.form.dispatch(new Event('submit'))}/>
<input type="hidden" name="comments" value="A really lit study plan!" />
<input type="hidden" name="approved" value=true />
<input type="hidden" name="uploaded_by" value="Name"/>
<input type="hidden" name="date_uploaded" value="2012-02-1"/>
<input type="hidden" name="university" value="australian_national_university"/>
</form>
While changing the file input, It will trigger the form submit (onStudyPlanUpload).
Hope this will work!
I'll answer my own question. If anyone else comes across this problem, it is the BACKEND that's faulty. This is my final solution using busboy to handle incoming form data. I didn't change anything on my server, but my router had to be updated. Here is my router, taking care of POST requests:
const express = require("express");
const mongoose = require("mongoose");
require('./../../models/Test');
const path = require("path");
const fs = require("fs");
const router = express.Router();
const Busboy = require("busboy");
router.route("/add").post((req, res, next) => {
let busboy = new Busboy({headers: req.headers});
// A field was recieved
busboy.on('field', function (fieldname, val, valTruncated, keyTruncated) {
if (req.body.hasOwnProperty(fieldname)) { // Handle arrays
if (Array.isArray(req.body[fieldname])) {
req.body[fieldname].push(val);
} else {
req.body[fieldname] = [req.body[fieldname], val];
}
} else { // Else, add field and value to body
req.body[fieldname] = val;
console.log(req.body);
}
});
// A file was recieved
busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
console.log("File incoming: " + filename);
var saveTo = path.join('.', filename);
console.log('Uploading: ' + saveTo);
file.pipe(fs.createWriteStream(saveTo));
});
// We're done here boys!
busboy.on('finish', function () {
console.log('Upload complete');
res.end("That's all folks!");
});
return req.pipe(busboy);
});
module.exports = router;
Finally, my finished onStydyPlanUpload() function!
onStudyPlanUpload(files) {
const file = files[0];
let formData = new FormData();
formData.append("pdf", file, file.name);
formData.append("comments", "A really lit study plan!");
formData.append("approved", true);
formData.append("uploaded_by", "Melker's mamma");
formData.append("date_uploaded", new Date());
formData.append("university", "australian_national_university");
const HOST = "http://localhost";
const PORT = 4000;
axios.post(`${HOST}:${PORT}/test/add`, formData)
.then(res => console.log(res))
.catch(err => console.log(err))
}
Got help from: https://gist.github.com/shobhitg/5b367f01b6daf46a0287