How to post object using fetch with form-data in React? - javascript

I want to send a file to my express app as backend. My problem is that my body is send as type application/json and I want to send this as a form-data and later upload this file using multer -> https://github.com/expressjs/multer
I don't know how to prepare fetch call to get it later in express.
fetch('/api/data', {
method: 'POST',
headers: {
Accept: 'application/form-data',
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then((resp) => resp.json())
.then((data) => console.log(data));
When I want to log req.file in api breakpoint I'm getting undefined.
app.post('/api/data', upload.single('cv'), (req, res) => {
console.log(req.body);
console.log(req.file);
res.send({ name: 'Hello world!' });
});
I store react data from form using hooks and this object looks like this:
{
name: 'test',
surname: 'test2',
cv: 'C:\\fakepath\\Babel error.png'
}

You need to build your request body using the FormData API.
The FormData interface provides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to "multipart/form-data".
const myFile = document.querySelector("input[type=file]").files[0];
const data = new FormData();
data.append("myFile", myFile);
data.append("otherStuff", "stuff from a text input");
fetch(target, {
method: "POST",
body: data
});

Related

Send an Object with Blobs in it to server

I have a data object containing strings, booleans, arrays, objects and even Blobs like this:
Now I want to send this object to my Node.js server...
Using traditional fetch API or Ajax I'm unable to send the blobs and they appear like an empty object server side!
I tried to use fetch API even using FormData approach like:
Client:
const fd = new FormData();
fd.append("upload", data); // data is the object to send
fetch('/mother/new-card', {
method: 'post',
body: fd,
})
Server:
controller side:
router.post('/new-card', newCard);
function newCard(req, res) {
motherService.newCard(req)
.then(result => res.json(result))
.catch(err => {
console.log(err);
res.json(err)
});
}
Service side:
async function newCard(req) {
console.log('req.body', req.body);
return { success: req.body }
}
I usually get undefined trying above methods ...
Is there any valid way to send an object with blobs in it?

Unable to get video file for S3 upload. (using Expo Camera)

I have been stuck with trying to upload a video to S3 for a while and was hoping to get some pointers. Currently, what I've read and was told is that we need to send an actual file to S3 and not the url (which we might do if we were sending it to the backend before aws).
I am trying to do this by
const getBlob = async (fileURi) => {
console.log('THIS IS IT', fileURi);
const resp = await fetch(fileURi);
const videoBody = await resp.blob();
console.log(videoBody);
};
getBlob(video.uri);
The problem I am having is I am unable to actually get the video file. When I stop recording a video with await camera.stopRecording(); what I get in return is
Object {
"uri": "file:///path/20DD0E08-11CA-423D-B83D-BD5ED40DFB25.mov",
}
Is there a recommended approach in order to successfully get the actual file in order to send it to S3 through the client?
The way I am trying to currently send the video which doesn't work is:
const formData = new FormData();
formData.append('file', video.uri);
await fetch(url, {
method: 'POST',
body: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
url: refers to the presignedUrl we get in return from aws.
P.S - Sending to the server through a fetch call does work but I noticed this approach also leave the User waiting for 10+ seconds since I need to send the video to the server then wait for it to finish uploading in AWS.
Thank you for all the help.
If I understand correctly you know how to upload file to your own server, but you want to send it directly to S3.
In that case I would suggest to use presigned URLs. https://docs.aws.amazon.com/AmazonS3/latest/dev/PresignedUrlUploadObject.html
You can generate presigned URL on your backend, it is basically regular URL pointing to S3 file and some key values. You need to send those values to mobile app and do the same fetch call you are already using, but replace url with the one generated on backend and add all key-values to FormData.
Example for node backend would look like this
import AWS from 'aws-sdk';
...
const client = new AWS.S3(config);
...
const presignedUrl = client.createPresignedPost({
Bucket: 'example-bucket-name',
Fields: { key: 'example-file-name' },
});
and in mobile app you would
const form = new FormData();
Object.keys(presignedUrl.fields).forEach(key => {
form.append(key, presignedUrl.fields[key]);
})
form.append('file', fileToUpload);
await fetch(presignedUrl.url, {
method: 'POST',
body: form,
headers: {
'Content-Type': 'multipart/form-data'
}
})
My solutions. Please review the sample application. https://github.com/expo/examples/tree/master/with-aws-storage-upload
const response = await fetch(video.uri);
const blob = await response.blob();
const params = {
Bucket: myBucket,
Metadata: {
long: long.toString(),
lat: lat.toString(),
size: videoSize.toString()
},
Key: myKey,
Body: blob
};

How can i upload image to server using axios in react native?

I want to upload an image file to the server in react native, how can i do so?
Here is my code :
In my index.js Which is my entry point I have set axios default configurations :
axios.defaults.baseURL = BaseUrl;
axios.defaults.headers.common['Content-Type'] = 'application/json';
axios.defaults.headers.common['Accept'] = 'application/json';
Now in my profileEdit.js, where I need to upload a profile image
let data = new FormData();
data.append('profile_picture',
{uri: imageResponse.uri, name: imageResponse.fileName, type: 'image/jpg'});
// imageResponse is a response object that I m getting from React native ImagePicker
axios.post('profile/picture/', data,
{
headers: {
"Authorization": "JWT " + this.state.token,
'content-type': 'multipart/form-data',
}
}).then(response => {
console.log('Profile picture uploaded successfully', response);
}).catch(error => {
console.log('Failed to upload profile image', error);
console.log('Error response',error.response);
});
But this giving me network error, while other APIs are working fine.
I followed the solution given here How to upload image to server using axios in react native?
This is the error response I am getting.
And my request header is like this
I don't want to use any other package such as react native fetch blob
More links that I followed :
axios post request to send form data
Can anyone please tell me where I m doing wrong or how shall I approach to this problem. ty
I think your header for the Axios POST request may be incorrect since you're using a FormData() constructor to store your image data:
You may want to try the following instead:
let data = new FormData();
data.append('profile_picture',{
uri: imageResponse.uri,
name: imageResponse.fileName,
type: 'image/jpg'}
);
axios.post('profile/picture/', data,
{
headers: {
'content-type': `multipart/form-data; boundary=${data._boundary}`,
...data.getHeaders()
}
}
)
For me simply work
const oFormData = new FormData();
oFormData.append("image", {
uri: images.uri,
type: images.type,
name: images.fileName
});
axios.post(servicesUrl.imageupload, oFormData);

Doing a PATCH with a file and some fields using multipart/form-data

I am doing a request to my server to patch some regular form fields as well as upload an image.
If I do these alone in separate requests all works perfectly, but nothing works if the form data and the file are in the same request. This is what it currently looks like:
const file = new FormData()
file.append('uploadedFile', uploadedFile, uploadedFile.name)
const patch = {
name: 'Some Name',
age: 18,
file
}
return isomorphicFetch(`${MY_URL}`, {
headers: {
Accept: 'application/json'
}
method: 'PATCH',
body: patch
})
Here is what my middleware looks like:
const multipartMiddleware = multer({ dest: 'uploads/' })
app.use('/myendpoint',
multipartMiddleware.single('uploadedFile'),
(req, res, next) => {
console.log('REQ', req)
req.feathers.file = req.file
next()
}
)
Here is the thing: if I only put file as the body of my PATCH, it works just fine, I will have a req.file object in the middleware as expected, however, with the patch object I show in my example, I don't have a req.file object. What am I doing wrong? I would like to keep both my files and the name and age fields.
Keep in mind that if I split it into two calls, it works just as expected. For it to work, it currently looks like this:
// This code is the only way I can make my patches work:
const file = new FormData()
file.append('uploadedFile', uploadedFile, uploadedFile.name)
const patch = {
name: 'Some Name',
age: 18,
}
return Promise.all([
isomorphicFetch(`${MY_URL}`, {
headers: {
Accept: 'application/json'
},
method: 'PATCH',
body: patch
}),
isomorphicFetch(`${MY_URL}`, {
headers: {
Accept: 'application/json'
},
method: 'PATCH',
body: file
})
])
Update: I tried doing this through Postman, and it did in fact work. So I am even more confused now.

Post form data with axios in Node.js

I'm testing out the Uber API on Postman, and I'm able to send a request with form data successfully. When I try to translate this request using Node.js and the axios library I get an error.
Here is what my Postman request looks like:
The response I get is: { "error": "invalid_client" }
Here is what I'm doing in Node.js and axios:
var axios = require("axios");
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
axios.post('https://login.uber.com/oauth/v2/token', {
client_id: '***',
client_secret: '***',
grant_type: 'authorization_code',
redirect_uri: 'http://localhost:8080/',
code: '***'
}, config)
.then(function(response) {
console.log(response.data)
})
.catch(function(error) {
console.log(error)
})
When I do this, I get a 400 response.
I added the 'multipart/form-data' header because I filled out the form-data in the Postman request. Without the header I get the same result.
I'm expecting to get the same response I'm getting from Postman, is there something wrong with my config variable in the Node.js script?
Any help would be appreciated!
You might be able to use Content-Type: 'application/x-www-form-urlencoded'. I ran into a similar issue with https://login.microsoftonline.com where it was unable to handle incoming application/json.
var axios = require("axios");
axios({
url: 'https://login.uber.com/oauth/v2/token',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: `client_id=${encodeURIComponent('**')}&client_secret=${encodeURIComponent('**')}&grant_type=authorization_code&redirect_uri=${encodeURIComponent('http://localhost:8080/')}&code=${encodeURIComponent('**')}`
})
.then(function(response) {
console.log(response.data)
})
.catch(function(error) {
console.log(error)
})
You could also use a function to handle the translation to formUrlEncoded like so
const formUrlEncoded = x =>
Object.keys(x).reduce((p, c) => p + `&${c}=${encodeURIComponent(x[c])}`, '')
var axios = require("axios");
axios({
url: 'https://login.uber.com/oauth/v2/token',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: formUrlEncoded({
client_id: '***',
client_secret: '***',
grant_type: 'authorization_code',
redirect_uri: 'http://localhost:8080/',
code: '***'
})
})
.then(function(response) {
console.log(response.data)
})
.catch(function(error) {
console.log(error)
})
Starting with Axios 1.3, you can send multipart/form-data data using FormData:
const axios = require('axios');
const form = new FormData();
form.append('my_field', 'my value');
form.append('my_other_field', 'my second value');
axios.post('http://example.com', form)
FormData is available on Node 17.6.0 (or newer), on older versions you'll have to use a polyfill such as form-data.
If you're using older versions of both Node and Axios, you have to set the Content-Type header yourself as well:
const axios = require('axios');
const FormData = require('form-data');
const form = new FormData();
axios.post('http://example.com', form, { headers: form.getHeaders() })
To send data with Content-Type application/x-www-form-urlencoded, wrap your {} with new URLSearchParams(). Like this snippet:
const axios = require("axios");
axios
.post("https://api.zipwhip.com/user/login", new URLSearchParams({
username: "hello",
password: "byby",
}))
.then((res) => console.log(res.data));
As for 10 June 2017, axios library does not support posting form data in Node.js. Here is the issue on GitHub - https://github.com/mzabriskie/axios/issues/789
We had the similar problem and decided to switch to request library.
This use case is now clearly documented with multiple solutions in the axios docs: https://axios-http.com/docs/urlencoded#form-data
From the error it seems your client_id or client_secret is incorrect. Enable debugging and share the raw request/response (after filtering credentials out).
It is not true! You can post data with axios using nodejs. I have done it. The problem is, if you use PHP on the server side, there is a pitfall you need to be aware of. Axios posts data in JSON format (Content-Type: application/json) PHP's standard $_POST array is not populated when this content type is used. So it will always be empty. In order to get post parameters sent via a json request, you need to use file_get_contents("http://php://input") .
A simple PHP script on the server side would be:
if($_SERVER['REQUEST_METHOD']==='POST' && empty($_POST)) {
$_POST = json_decode(file_get_contents('http://php://input'));
}
Using this method you can avoid the formData dependency. You CAN post data directly from node.js.

Categories