How can i add values from the front end to a json file in a react application? - javascript

I have built a react application where I have created a file upload mechanism. Now along with the file upload there are three dropdowns 'Controller','Test' and 'Protocol'. Now along with the file upload to the server functionality I am enabling the user to add the values to the dropdown from the front-end itself. I have enforced few validations on the file which is getting uploaded and then
I am adding the controller, test and protocol based on the file name. SO for instance if the filename of the uploaded file is "abc_xyz_sas_uut.config" which is the variable Filename in the script then the three fields 'Controller','Test' and 'Protocol' should only accept 'abc','xyz' and 'sas' as the respective values.
I have written the below script but its getting me an error 'Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client'. The problem is I guess that its setting the status twice. Can anyone help to fix this issue, any help is greatly appreciated as I am stuck at this point.
class FileUpload extends Component {
state = {
file: "",
fileName: "Choose File",
uploadDisabled: true,
controller: "",
test: "",
protocol: "",
};
handleFileChange = (e) => {
let index = e.target.files[0].name.search(/\w+[_]\w+[_]\w+[_]uut.yaml-example/i);
if (index === 0) {
this.setState({ file: e.target.files[0] });
this.setState({ fileName: e.target.files[0].name });
this.setState({ uploadDisabled: false });
} else {
window.alert(
"File name or format is not correct ! \nCorrect format is controller_test_protocol_uut.yaml-example"
);
}
e.target.value = null;
};
handleUpload = async (e) => {
console.log("Starting upload");
const formData = new FormData();
formData.append("file", this.state.file);
formData.append("controller", this.state.controller);
formData.append("test", this.state.test);
formData.append("protocol", this.state.protocol);
try {
const res = await axios.post(this.props.url + "/upload", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
const { status } = res.data;
if (status === "ok") {
this.props.refreshOptions();
this.setState({ file: "" });
this.setState({ fileName: "Choose File" });
this.setState({controller : ""});
this.setState({test : ""});
this.setState({protocol : ""});
alert("File Uploaded Successfully");
document.getElementById("closeButton").click();
}
} catch (err) {
console.log(err);
}
};
handleTextChange = (e) => {
this.setState({ ...this.state, [e.target.id]: e.target.value });
};

You have a res.json() statement in your else-block:
//Writing Config back to disk
}
else {
res.json({
status: "Invalidvalues"
});
}
and right after
res.json({
status: "ok"
});
So in case of hitting the else block res.json() is executed twice which is probably the cause for the error. You should change the statement in the else block to:
return res.json({
status: "Invalidvalues"
});

Related

[filepond]To register with the ID value serverId received from the server

'filepond' is being used in vue. But there's a problem.
In "process",
The ID value was returned after the file was transferred to the server. (response.id) If you register this as 'serverId' of file and check 'file' as console, it is registered normally.
mounted() {
setOptions({
server: {
process: (fieldName, file, metadata, load, error, progress, abort) => {
const formData = new FormData();
formData.append('mainData', file);
createmainData(formData, {
onUploadProgress: (event) => {
progress(event.lengthComputable, event.loaded, event.total);
}
})
.then(response => {
const serverId = response.id
file.serverId = serverId
load(response, serverId)
})
.catch(error => {
console.log(error);
error('Error uploading file');
})
console.log (file)
},
},
});
}
But if you run 'updatefiles' methods and check 'files' as a console,
It says 'serverId: undefined'.
methods: {
updatefiles(files) {
this.files = files.map(files => files.setMetadata);
console.log('files',files )
},
},
If you look at the comments left by the producers,
I tried, but I keep failing, maybe I'm misunderstanding.
Can you tell me the solution?
You have to push serverId to separate array (i.e. uploadedFiles) in the process after the file was successfully transfered to the server. I can't see this in your code. With this array you will be able to manage FilePond content and order of uploaded files.
handleFilePondInit() {
this.uploadedFiles = [];
},
handleFilePondLoad(response) {
this.uploadedFiles.push(response);
return response;
},
handleFilePondUpdate(files, origin, target) {
this.uploadedFiles = files.map(files => files.serverId);
this.uploadedFiles = this.item.uploadedFiles.filter(element => { return element !== null; });
},

Why is URL.creatObjectURL(blob) giving a cross-origin frame error in NodeJS/React application

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.

How to upload Image to Cloudinary - MERN Stack

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.

throw er; // Unhandled 'error' event Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

i tried to upload product (including the image), i used the same code on this video https://www.youtube.com/watch?v=GCmjLIEtJbA&t=206s, but its failed to upload product ?, but the server give me a 200 res, then the express app goes crash, could you help me with this issues ??
this is my UploadPage component (React) :
const onSubmit = (event) => {
event.preventDefault();
// send data to server
const variables = {
writer: props.user.userData._id,
title: TitleValue,
description: DescriptionValue,
price: PriceValue,
images: Images,
continents: ContinentValue,
};
Axios.post("/api/product/uploadProduct", variables).then((response) => {
if (response.data.success) {
alert("Successfully to upload product");
props.history.push("/");
} else {
alert("Failed to upload product");
}
});
};
and here for the product route (Express) :
router.post("/uploadProduct", auth, (req, res) => {
// save all data from the client to database
const product = new Product(req.body);
product.save((err) => {
// if fail
if (err) {
return res.status(400).json({ success: false, err });
}
// if success
return res.json(200).json({ success: true });
});
});
here's also my FileUpload component (React) :
const onDrop = (files) => {
let formData = new FormData();
const config = {
header: {
"content-type": "multipart/form-data",
},
};
formData.append("file", files[0]);
// save the image inside the node server
Axios.post("/api/product/uploadImage", formData, config).then(
(response) => {
if (response.data.success) {
setImages([...Images, response.data.image]);
props.refreshFunction([...Images, response.data.image]);
} else {
alert("Failed to save the Image in server");
}
}
);
};
did the front end messed up the back end ?,
Thanks, sorry for my bad english.
Solved by myself, somewhere in my (Express) code there is a code like this:
const storage = multer.diskStorage({
// where i want to save the file
destination: (req, file, cb) => {
cb(null, "uploads/");
},
// more code
});
then i change it into :
const storage = multer.diskStorage({
// where i want to save the file
destination: (req, file, cb) => {
cb(null, "./uploads/");
},
// more code
});

What would be the equivalent code in reactjs for this angularjs code

I'm working on a website which was based on angularjs. Now I want to convert few snippets into reactjs and I never used angular so obviously I'm having problem understanding few of the codes written in angularjs. I understand some of the code written here as it is used to save a post and show error when it's not saved. but i don't understand $scope and how to convert this piece of code to react. I hope someone could help me
$scope.savepost=function(){
$scope.postdata={}
$scope.postdata['postTitle']=$scope.postTitle
$scope.postdata['postDescription']=$scope.postDescription
console.log($scope.postId)
if($scope.postId==null){
return $http.post('/api/saveposts',$scope.postdata).then(function(response){
if(response.status==200){
$scope.postId=response.data;
toaster.pop('success','post saved successfully!')
}else{
toaster.pop('danger','An error has occured while saving the post. Please try again')
}
});
}else{
$scope.postdata['postId']=$scope.postId
return $http.post('/api/updateposts',$scope.postdata).then(function(response,status){
if(response.status==200){
toaster.pop('success','post saved successfully!')
}else{
toaster.pop('danger','An error has occured while updating the post. Please try again')
}
});
}
}
It would look something like this:
// destructure postId from props
const SomeFormComponent = ({ postId }) => {
const [postState, setPostState] = useState({title: '', description: ''})
/*
This example assumes you've set your input values to postState
*/
const handleRequest= async (url) => {
// copy all the input values set to state
const post = {...postState}
// this will be passed into fetch
const request = {
method: 'POST'
headers: {
'Content-Type': 'application/json'
}
}
if(postId != null) post['id'] = postId
try {
// use fetch, stringify json
const response = await fetch(url, { ...request, body: JSON.stringify(post )})
// handle json response
const data = await response.json()
if (data.status == 200) {
toaster.pop('success', 'post saved successfully!')
/*
Do something with the response
*/
} else {
toaster.pop('danger', 'An error has occurred while updating the post. Please try again')
}
} catch(ex) => {
console.error(ex.stack)
toaster.pop('danger', 'An error has occurred while updating the post. Please try again')
}
}
const handlePost = () => {
if(postId == null) {
return handleRequest('/api/savepost')
}
return handleRequest('/api/updatepost')
}
return (<button onClick={handlePost}>Save</button>)
}

Categories