Multipart form Response from NodeJS Server- issue extracting some fields - javascript

I have a ReactJS Application that sends an Axios POST request to a NodeJS express server and expects a response with several fields. Specifically, I am using "form-data" in the backend to handle the data streaming.
Here is my backend response.
var FormData = require('form-data');
app.get('/test', (req, res) => {
// create a new form to send all data back to client
var form = new FormData();
form.append('results_log', 'this is text', { contentType: 'text/plain'});
form.append('results_images', fs.createReadStream('./test_results/result.zip'), { contentType: 'application/zip' });
console.log(form);
form.pipe(res);
});
In the frontend, I can download the "results_images" ZIP file from the formdata server response without any issues, but I cannot get the "results_log" field, which is just a simple string. Every attempt has resulted in a "null".
Below is the frontend code.
axios({
url: 'http://localhost:5000/test',
method: 'GET'
})
.then((response) => {
var info = response;
// this alerts NULL
alert(info.data["results_log"]);
const url = window.URL
.createObjectURL(new Blob([info.data["results_images"]]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'results.zip');
document.body.appendChild(link);
link.click();
})
.catch((error) => console.log(error));
Here is part of the alert of the whole response.data field. As you can see, both fields are present in the response.
Am I doing something wrong when extracting the string field? Any help is appreciated!

Related

multiple asynchronous post request with files in nodejs

I've tried to send a bunch of post requests with file upload in nodejs.
using axios.post, I could make a single request easily. But I got an error when trying send multiple asynchronous requests.
Based on the axios document, it uses axios.all([ axios.get(), axios.get(), ...]) to make async requests at time.
If I sent my code, the error says:
"Error: Request failed with status code 500 ~ "
. This error is the same when I send a request without file upload. So I guess my code doesn't attach a file when I send async request.
Please advise me what I am missing.
My code is below:
var axios = require('axios');
var FormData = require('form-data');
var fs = require('fs');
var data = new FormData();
data.append('files', fs.createReadStream('./liscense.jpg'));
var config = {
method: 'post',
url: 'https://domainname/scan/id',
headers: {
...data.getHeaders()
},
data : data
};
axios
.all([axios(config), axios(config)])
.then(
axios.spread((res1, res2) => {
console.log(res1.data, res2.data);
})
)
.catch((error) => {
console.log(error);
});
Your problem is that you are sending a empty stream,
There is an "_streams" array in your form data that contains the stream of your "liscense.jpg" file, and when you POST the first request to your target host, this stream will be empty and the stream of your other requests is empty, so the file does not reach your destination.
In short, this code only send your file once in first request, and other requests do not include your file/files.
you can try this:
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
function postRequest() {
const data = new FormData();
data.append('files', fs.createReadStream('./liscense.jpg'));
const config = {
method: 'post',
url: 'https://domainname/scan/id',
headers: {
...data.getHeaders()
},
data: data
};
return config;
}
axios
.all([axios(postRequest()), axios(postRequest())])
.then(
axios.spread((res1, res2) => {
console.log(res1.data, res2.data);
})
)
.catch((error) => {
console.log(error);
});

I can't POST FormData Object from user uploaded file with Axios (React, Node.JS)

I'm trying to allow users to upload pictures to my application (React frontend, Node backend). I want to send the uploaded file from the client to the server, then make a request from my server to Firebase cloud functions that will deal with storing the image.
My HTML for the form is as follows:
<div className='LoafGalleryWrap'>
<h1 className='banner'>Illustrious Loafs</h1>
<form>
<label>Add a loaf to the exhibit:
<input type='file' onChange={props.fileSelectedHandler} />
<button onClick={props.fileUploadHandler}>Upload</button>
</label>
</form>
<button onClick={props.toggleDisplayLandingPage}>Back</button>
</div>
I get the file data in my app with the following method. In the console.log, I can see the file data I want to send to my server.
fileUploadHandler(e) {
e.preventDefault();
e.stopPropagation();
let file = this.state.selectedFile;
let formData = new FormData();
formData.append('image', file);
formData.append('name', file.name);
const pic = formData.getAll('image');
console.log(formData)
axios({
url: 'http://localhost:3001/uploadLoaf',
method: 'POST',
data: formData,
headers: { "Content-Type": "multipart/form-data" }
})
.then(respo => console.log(respo))
}
In my server, when I check the request body, there is nothing there. I log it out and simply get an empty object. I've tried using multer and connect-multipart middlewares but none have been successful in showing the form data. What I am doing is similar to what this article suggest (https://www.geeksforgeeks.org/file-uploading-in-react-js/), but I am at a loss for how to get the data sent in my forms. Any guidance much appreciated.
My server route is as follows:
app.post('/uploadLoaf', (req, res) => {
console.log('uploadLoaf endpoint reached');
console.log(req.body);
res.status(201).json('complete')
// axios.post('https://us-central1-sour-loafs.cloudfunctions.net/uploadFile/', 'upload_file', formData, {
// headers: {
// 'Content-Type': 'multipart/form-data'
// }
// })
// .then(result => {
// console.log(result);
// res.send(result);
// })
// .catch(err => {
// console.log(err);
// res.send(err)
// })
});
try to replace the axios function you have with this
axios.post("http://localhost:3001/uploadLoaf", formData, { })
.then(res => { // then print response status
console.log(res.statusText)
})
let me know what you use in the backend and what is the result on res function
I managed to access the FormData file data by using Multer's upload function. My imports and server route configured so that I can access the file data is as follows:
const multer = require("multer");
const upload = multer({
limits:{fileSize: 1000000},
}).single("image");
app.post('/uploadLoaf', (req, res) => {
upload(req, res, (err) => {
if (err) {
res.sendStatus(500);
}
console.log(req.file);
res.send(req.file);
});
});
I am now unsure of how to send this file data to the API in the way the API expects, getting an error stating: "Error: could not handle the request\n". But at least I can now see the data from the form.

AWS S3 update image using pre-signed URL (Axios-PUT Request)

I'm trying to update a local JPG image file into an S3 bucket using the REST PUT request and Axios.
I managed to send the PUT request and to get a positive answer from AWS S3 Service but what it's been upload is not a JPG file but a JSON file.
This is the code that I'm using:
//Create the FormData
var data = new FormData();
data.append('file', fs.createReadStream(image_path));
//Send the file to File-system
console.log("Sending file to S3...");
const axiosResponse = await axios.put(image_signed_url, {
data: data,
headers: { 'Content-Type': 'multipart/form-data' }
}).catch(function(error) {
console.log(JSON.stringify(error));
return null;
});
I have already try to change the headers to {'Content-Type': 'application/octet-stream' } but I obtained the same result.
It did not manage to make AXIOS work in order to upload an image.
The node-fetch module did it sending the image as a binary and specifying the "Content-type".
If I try to the same using AXIOS it the image was always packet into a form-data and the result was JSON file uploaded into the S3 bucket instead of the image.
//Send the file to File-system
console.log("Sending file to S3...");
const resp = await fetch(image_signed_url, {
method: 'PUT',
body: fs.readFileSync(image_path),
headers: {
'Content-Type': 'image/jpeg',
},
}).catch( err => {
console.log(err);
return null;
});

How to get text value from multipart/form-data

I'm building a React app, and one of the components is creating a FormData object with two fields - one is a file and the other is a string. The FormData is being sent as a PUT method to an express route, and its type is multipart/form-data because there's a file to upload. There I need to get the string valie from the form (called path), then use multer/multer-s3 to upload the file to AWS S3 to the specified path.
I'm stuck on how to do this. I wasn't able to find an answer on how to retrieve a text field from a multipart request. I did see How to Post "multipart/form-data" Form and get the Text field values from the Node.js server? and several similar suggestions, however the suggested answer did not work for me.
On the body-parser website, it says "This does not handle multipart bodies" - so that also means that bodyParser.urlencoded wouldn't help in my case.
React component:
const path = `/somepathhere/`;
const formData = new FormData();
formData.append('file', file);
formData.append('path', path)
// call API handler
apiActions.js:
const response = await fetch(apiUrl, {
method: "PUT",
body: formData
})
server.js:
// other code taken out for brevity
// middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// other code taken out for brevity
app.put('/api/uploadToS3', (req, res) => {
const path = '';
const storage = multerS3({
s3: s3,
bucket: process.env.AWS_S3_BUCKET,
contentType: multerS3.AUTO_CONTENT_TYPE,
cacheControl: 'max-age=31536000',
metadata: function (req, file, cb) {
cb(null, {fieldName: file.fieldname});
},
key: function (req, file, cb) {
cb(null, path + file.originalname)
}
})
const upload = multer({
storage: storage
}).any();
upload(req,res,function(err) {
if(err) {
console.log(err);
return res.end("Error uploading file.");
} else {
res.end("File has been uploaded");
}
});
});
If there is a better way to handle this, I would also like to know. The end goal is that the file should be uploaded to an S3 path dynamically determined in the React app. I tried sending an object with formData and path like such:
const response = await fetch(apiUrl, {
method: "PUT",
body: {formData: formData, path: path}
})
But that gave the error: [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client. Trying to add headers to the fetch request likewise gave various errors.

res.download() download window not showing up

I created a download button that downloads the file on click. The problem is when I click the download button, I'm able to see the content that I want to download by using Chrome inspect -> Network -> Response but it is not opening a window to save the file to my PC.
For example, I'm trying to download text.txt which contains multiple lines of MY FILE string. I'm able to see it on Response tab but how can I download the .txt file.
Relevant React Code:
<button onClick={(e) => downloadHandler(e)}>Download</button>
let downloadHandler = (e) =>{
const fileName = e.target.parentElement.id;
fetch('http://localhost:3001/download',{
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({id: fileName})
})
}
Relevant Backend Code:
const uploadFolder = `${__dirname}` + `\\uploads\\`;
app.post('/download', function(req, res){
let fileName = req.body.id; // example output: 'dog.jpg'
res.download(uploadFolder+fileName, fileName); // Set disposition and send it.
});
The idea is that I will feed fileName to backend and then download it with res.download(uploadFolder+fileName, fileName); line. I think im suppose to use window.open('/download') but that just opens the homepage on a new tab or maybe I am just placing it wrong.
Okay, I have managed to solve my issue. three main modifications were made to make this code and idea work.
Changing the request from POST to GET. Another
StackOverflow thread also mentions this.
Using axios() instead of fetch().
Creating Blob object from the res.download(filePath, fileName) response value.
Anyone else having this problem with the React Code part should check this Github link.
Final State of the React function posted in the question
let downloadHandler = (e) =>{
const fileName = e.target.parentElement.id;
axios({
method: 'get',
url: 'http://localhost:3001/download/'+fileName,
responseType: 'blob',
headers: {},
})
.then((res) => {
const url = window.URL.createObjectURL(new Blob([res.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
})
.catch((error) => {
alert(error);
})
}
Final State of the backend code posted in the question
const uploadFolder = `${__dirname}` + `\\uploads\\`;
app.get('/download/:filename', function(req, res){
let fileName = req.params.filename
const filePath = uploadFolder+fileName;
res.download(filePath, fileName);
});

Categories

Resources