How to make a multipart request from node JS to spring boot? - node.js

I have a node js backend that needs to send a file to a spring boot application.
The file is local uploads/testsheet.xlsx.
Here is the code to upload the file using the form-data npm module and Axios.
const formData = new FormData()
formData.append("file", fs.createReadStream("uploads/testsheet.xlsx"));
formData.append("status", status);
const path = `/endpoint`
const auth = {
username: username,
password: password
}
const url = `${baseUrl}${path}`
const response = await sendRequest(url, auth, "POST", formData, null, null, null)
//sendRequest Code -
try {
const response = await axios({
method,
url,
data,
params,
headers,
auth,
config,
})
return response
} catch (err) {
console.log(`Fetch Error: => ${method}:${url} => ${err.message || err.response}`)
return err.response ? { error: err.response.data.message } : { error: err.message }
}
The Spring boot side has the following code -
#PostMapping("/endpoint")
public StatusResponse update(#RequestParam("file") MultipartFile file, #RequestParam("status") String status){
boolean response = transactionService.update(file, status);
return statusResponse;
}
On the spring boot side, I keep getting this error -
org.springframework.web.multipart.MultipartException: Current request is not a multipart request
While sending from the postman, everything works correctly.!!!
How do I send the multipart file successfully?

You should use the .getHeaders() function that the formData object provides, in order to correctly set the multipart form boundary, e.g:
const res = await axios.post(url, formData, {
headers: formData.getHeaders()
});

Related

How to send a file to backend in react-native using node.js

I tried
const myData = uri; // this looks like file:///data/expo/...
const myDataResponse = await API.SendFile({
myData: myData
});
But file is not being recieved in the backend. Is there any other way to do this?
IN the backend I get
console.log(req.body)
// I get a string same as uri of file
console.log(req.file)
// undefined
Backend is setup perfectly. I am getting successful response when using postman.
Any help is appreciated. I am a begginer.
You should use FormData.
const data = new FormData();
// first argument is the key name received on you API
// second argument is your file path
data.append('file', filePath)
// then your request should look like this :
await fetch(url, {
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
body: data,
})

form-data request body is empty

I'm trying to send an image and some data from an API to another. The image is stored in memory with multer. But when I want to send it, the body is just empty. I tried the same request with postman and it worked perfectly.
postman test
postman test image
server test
server test image
Here is some code. I removed some of it so you can read it better
export const saveImage = async ({ image, name, folder, options }: { image: any, name?: any, folder: string, options?: any }) => {
try {
const fd = new FormData();
fd.append("image", image.buffer, image.originalname);
if(options) {
fd.append("options[resize][height]", options?.resize?.height);
fd.append("options[resize][width]", options?.resize?.width);
}
if(name) fd.append("name", name);
fd.append("folder", folder);
fd.append("servideId", IMAGES_ID);
fd.append("serviceSecret", IMAGES_SECRET);
console.log(fd)
const formHeaders = fd.getHeaders();
const request = await axios.post(`${IMAGES_URL}/api/images`, {
headers: formHeaders,
body: fd
});
return request.data.id;
} catch (error) {
const { response } = error;
console.log(response.request.data)
if(error?.response?.data?.error) {
throw { statusCode: error.response.status, message: error.response.data.error }
}
console.error("Images API", error);
throw new InternalError("Something gone wrong");
}
}
When I log the FormData, I can see in _streams, the data that I'm sending, but the Images API receives an empty body.
FormData screenshot
If you need more information tell me, please! Thank you
The axios API for the post method is: axios.post(url[, data[, config]]). The second argument must always be the data you send along.
In your case axios thinks { headers: formHeaders, body: fd } is the body and the request ends up being application/json. To send a file with data using axios in Node.js, do the following:
const response = await axios.post(`${IMAGES_URL}/api/images`, fd, {
headers: {
...formHeaders,
'X-Custom-Header': 'lala', // optional
},
});
Your question inspired me to turn this answer into an article — Send a File With Axios in Node.js. It covers a few common pitfalls and you'll learn how to send files that are stored as a Buffer or coming from a Stream.
With Axios, you can directly use the form data without having to deal with headers.
axios.post("/api/images", fd)
If you wish to modify headers at some point in the future, you should pass the formData to the `data` field instead of `body`.
axios.post("/api/images", { headers: formHeaders, data: fd })
Correction in comments.
It can also be done using the Axios API syntax.
axios({method: 'post', url: 'url', data: fd, headers: {} })
In the backend, multer will add your files to req.file instead of req.body, if you have properly configured it to do so.

I cannot send base 64 url encoded data to my nodejs backend

I am trying to send post request to backend containing base64url encoded value of my image. When I send request with any random string, the request is received at backend but when I try to do same thing using encoded value, it responds me back with
request failed with status code 500
My code for the request is:
const uploadFileHandler = async (e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
setPreviewSource(reader.result);
uploadtobackend(reader.result);
};
const uploadtobackend = async (filedata) => {
try {
const config = {
headers: {
'Content-Type': 'application/json',
},
};
console.log(config);
console.log(filedata);
const { data } = await axios.post(
`/api/uploads`,
{
data: filedata,
},
config,
);
setImages(data);
setUploading(false);
} catch (error) {
console.log(error);
setUploading(false);
}
};
};
Here filedata is the encoded value and it is displayed if I console it.
Instead of
data: filedata
If I send
data: "some random string"
then request reaches backend otherwise not.
You nedd to send your file by wrapping in FormData.
You can do something like
const fd = new FormData()
// data you want to send other than file
fd.append('payload',JSON.stringify(payload))
fd.append('file',file) // file you want to send to node backend
Now in your backed you can get this file using req.file
Probably you need to use JSON.stringify() here are some example of use
const { data } = await axios.post(
`/api/uploads`,
{
data: new Buffer(JSON.stringify(filedata)).toString("base64")
},
config
)

Using images from Google Street View Static API without exposing key

I am trying to include Street View images on a React app. Here is the documentation for the API:
Street View Static API
When I make a simple fetch request to google I get a response that contains a URL to the street view image as a JPG file:
bodyUsed:false
headers:Headers
ok:true
redirected:false
size:0
status:200
statusText:"OK"
Symbol(Body internals):Object {body: PassThrough, disturbed: false, error: null}
Symbol(Response internals):Object {url: "https://maps.googleapis.com/maps/api/streetview?lo…", status: 200, statusText: "OK", …}
timeout:0
// URL BELOW HERE IS WHAT I'M USING
url:"https://maps.googleapis.com/maps/api/streetview?location=3737%20N%20Southport%20Ave,%20Chicago,%20IL%2060613&key={MY_SECRECT_KEY}&size=300x300"
I can't use this image URL directly in my app because it contains my secret API key.
What I'm attempting to do now is use an AWS lambda function that uses Node to read the image file from the URL and then send this image file as an HTTP response to the client's react app.
I'm having trouble figuring out how to do this in Node. I've seen some code online about using Node's readFile function from the file system module. But I'm having trouble getting it to work.
Here's the code in my Lambda function.:
const fetch = require('node-fetch');
const fs = require('fs');
const autoCompURL = "https://maps.googleapis.com/maps/api/streetview?"
const { G_PLACES_KEY } = process.env
const key = `&key=${G_PLACES_KEY}`
const size = "&size=300x300"
function getSearch(e) {
const resp = JSON.parse(e.body)
return resp.place_address
}
async function googleResults(str) {
const response = await fetch(
`${autoCompURL}location=${str}${key}${size}`
)
return new Promise(resolve => resolve(response));
}
exports.handler = async function(event, context) {
try {
const userSearch = getSearch(event)
const autoResults = await googleResults(userSearch)
const imgURL = autoResults.url
const img = await fs.promises.readFile(imgURL)
if (autoResults.status !== "OK") {
// NOT res.status >= 200 && res.status < 300
return { statusCode: autoResults.status, body: autoResults.statusText, error_message: autoResults.error_message }
}
return {
statusCode: 200,
headers: {'Content-Type': 'image/jpeg'},
body: img
}
} catch (err) {
console.log(err) // output to netlify function log
return {
statusCode: 500,
body: JSON.stringify({ msg: err.message }) // Could be a custom message or object i.e. JSON.stringify(err)
}
}
}
Appreciate any clues on how I can get this to work.
Did you try this option ?
You can generate Digital Signature and use it along with API invocation. And limit unsigned requests to zero .
https://cloud.google.com/blog/products/maps-platform/google-maps-platform-best-practices-securing-api-keys-when-using-static-maps-and-street-view-apis

Issue uploading an image using axios

I'm trying to send an image to my server using axios with react-native.
For doing this, I'm passing the image data (the base 64 encoded image data) directly to an uploadPicture function which uses axios this way:
const uploadPicture = async (data): Promise<AxiosResponse<string>> => {
const response = publicApi.post(
`${API_URL}/upload`,
{
image: data,
},
{
headers: { 'Content-Type': 'multipart/form-data' },
transformRequest: [transformToFormData],
}
);
return response;
};
const transformToFormData: AxiosTransformer = data => {
const formData = new FormData();
for (const key in data) {
formData.append(key, data[key]);
}
return formData;
};
The issue I face :
I get an internal error, like if my image was not correctly transmitted through my request.
If I'm doing the exact same request using Postman, it works fine, setting the body like this :
Which make me think that the issue doesn't come from my server but from my axios request.
Any idea of what I could be doing wrong ? Am I missing any axios option somewhere ?
I managed to find a solution:
I used the fetch function from javascript instead of axios
I send a file object instead of the data directly
I had to disable the react-native network inspect, otherwise the upload won't work
My working solution below, image being the response of react native image picker:
const file = {
uri: image.uri,
name: image.fileName,
type: image.type,
size: image.fileSize,
slice: () => new Blob(),
};
const body = new FormData();
body.append('image', file);
const response = await fetch(`${API_URL}/upload`, {
method: 'POST',
body,
});

Resources