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

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
});

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 can i add values from the front end to a json file in a react application?

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"
});

AWS S3 file upload with Node.js: Unsupported body payload error

I am trying to get my node.js backend to upload a file to AWS S3, which it got in a post request from my front-end. This is what my function looks like:
async function uploadFile(file){
var uploadParams = {Bucket: '<bucket-name>', Key: file.name, Body: file};
s3.upload (uploadParams, function (err, data) {
if (err) {
console.log("Error", err);
} if (data) {
console.log("Upload Success", data.Location);
}
});
}
When I try uploading the file this way, I get an Unsupported Body Payload Error...
I used fileStream.createReadStream() in the past to upload files saves in a directory on the server, but creating a fileStream did not work for me, since there is no path parameter to pass here.
EDIT:
The file object is created in the angular frontend of my web application. This it the relevant html code where the file is uploaded by a user:
<div class="form-group">
<label for="file">Choose File</label>
<input type="file" id="file"(change)="handleFileInput($event.target.files)">
</div>
If the event occurs, the handleFileInput(files: FileList) method in the corresponding component is called:
handleFileInput(files: FileList) {
// should result in array in case multiple files are uploaded
this.fileToUpload = files.item(0);
// actually upload the file
this.uploadFileToActivity();
// used to check whether we really received the file
console.log(this.fileToUpload);
console.log(typeof this.fileToUpload)
}
uploadFileToActivity() {
this.fileUploadService.postFile(this.fileToUpload).subscribe(data => {
// do something, if upload success
}, error => {
console.log(error);
});
}
the postFile(fileToUpload: File) method of the file-upload service is used to make the post request:
postFile(fileToUpload: File): Observable<Boolean> {
console.log(fileToUpload.name);
const endpoint = '/api/fileupload/single';
const formData: FormData = new FormData();
formData.append('fileKey', fileToUpload, fileToUpload.name);
return this.httpClient
.post(endpoint, formData/*, { headers: yourHeadersConfig }*/)
.pipe(
map(() => { return true; }),
catchError((e) => this.handleError(e)),
);
}
Here is the the server-side code that receives the file and then calls the uploadFile(file) function:
app.post('/api/fileupload/single', async (req, res) => {
try {
if(!req.files) {
res.send({
status: false,
message: 'No file uploaded'
});
} else {
let file = req.files.fileKey;
uploadFile(file);
//send response
res.send({
status: true,
message: 'File is uploaded',
data: {
name: file.name,
mimetype: file.mimetype,
size: file.size
}
});
}
} catch (err) {
res.status(500).send(err);
}
});
Thank you very much for your help in solving this!
Best regards, Samuel
Best way is stream the file. Assuming you are. reading it from disk. You could do this
const fs = require("fs");
const aws = require("aws-sdk");
const s3Client = new aws.S3();
const Bucket = 'somebucket';
const stream = fs.createReadStream("file.pdf");
const Key = stream.path;
const response = await s3Client.upload({Bucket, Key, Body: stream}).promise();
console.log(response);

Using Multer to handle formdata

I have this code that creates a project and uploads the image to Amazon S3.
I am using Bodyparser middleware to handle the data coming from the client but after some research I found out that it doesn't handle formdata. I need to use multer or some library like that.
Here is my attempt to use multer:
In my routes folder that handles the Post request to the Create function, I added it like this:
import multer from 'multer';
const upload = multer();
routes.post(
'/',
upload.any('projectImage'),
ProjectController.create,
);
Here is the Create function:
export async function create(req, res, next) {
const body = req.body;
S3Upload(req, res, async (s3err, s3res) => {
if (s3err) {
res.send('Error occured uploading image')
} else if (s3res && s3res.Location) {
body.projectImage = s3res.Location
try {
return res
.status(HTTPStatus.CREATED)
.json(await Project.createProject(body, req.user._id));
} catch (err) {
err.status = HTTPStatus.BAD_REQUEST;
return next(err);
}
} else {
res.send('Error creating project.')
}
});
}
Now when I send a post request to the /project routes. I get this error:
(node:77236) UnhandledPromiseRejectionWarning: TypeError: (0 , _s2.default) is not a function
(node:77236) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
PS. The Create function works perfectly without adding the S3Upload. & the S3Upload also works perfectly if I am only sending a file. The only time when it doesnt work is when I send mix data. i.e. a form with some text fields and a file.
I didn't post the S3Upload function but if anyone is curious, let me know I will share the code for it.
UPDATE
export async function S3Upload(req, res, callback) {
const chunks = [];
let fileType;
let fileEncodingType;
const busboy = new Busboy({
headers: req.headers,
});
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
filename.replace(/ /g, "_");
fileType = mimetype;
fileEncodingType = encoding;
file.on('data', data => {
chunks.push(data)
});
file.on('end', () => {
console.log(`File [${filename}] Finished`);
});
});
busboy.on('finish', () => {
const userId = UUID();
const params = {
Bucket: BUCKET_NAME,
Key: userId,
Body: Buffer.concat(chunks),
ACL: ACL,
ContentEncoding: fileEncodingType,
ContentType: fileType
}
S3.upload(params, (err, s3res) => {
if (err) {
callback(err);
} else {
callback(null, s3res);
}
});
});
req.pipe(busboy);
}

Categories