Upload image through axios in React Native - javascript

I've been dealing with this problem for a while now apparently you can upload a image using FormData and appending the file but I am still unable to get this to work. I have already tried with the following threads:
React Native - Axios - Trying to upload image
form post with file attach throws network error / React Native + react native Image picker
How do I set multipart in axios with react?
My first problem revolves around FormData().append(), in most solutions a blob/file is configured using the uri property but I don't have access to this property (Is this related to Typescript?). So something like this:
formData.append("profile_photo", {
uri: values.imageURI
});
Here's the error TS is returning:
Argument of type '{ uri: string; }' is not assignable to parameter of type 'string | Blob'.
Object literal may only specify known properties, and 'uri' does not exist in type 'Blob'.ts(2345)
Another key points is setting up my axios client with the Content-Type header, I've done this during my POST request like so:
// Perform a profile post request
const profileResponse = await loginClient.post(
"/user_profile",
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
}
);
But by logging my request with Axios' interceptors, here how the request headers look like:
headers:
Accept: "application/json"
Authorization: "Bearer 36|8SVw1DntRKni0sUn4NDX9moLluCHyYcZSadfgI5B"
__proto__: Object
Just a few observations:
The image uri is changed using expo-image-picker.
I'm using Formik to build my form.
I'm using Typescript.

Solved by using the Form-Data module, then you can easily pass in the uri like so:
formData.append("profile_photo", {
uri: values.imageURI,
name: "image.png",
type: "image/png",
});
This could be improved by extracting the type from the uri tho.

You need to send these 3 values while uploading a file.
URI.
Name.
Type.
You can get all these values from the file picker.
Send it as
const formData = new FormData();
formData.append("profile_photo", {
uri: values.imageURI,
name: values.name || "profile_photo.png",
type: values.type || "image/png"
});
axios.post('upload_file', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});

Related

How to send a POST request as form-data when FormData is unavailable? (Airtable scripts)

The Cloudinary API requires to send data using multipart/form-data, but I'm working in an environment where FormData is not available.
How could I do something as simple as this, then?
const formData = new FormData();
formData.append('file', assetUrl);
formData.append('upload_preset', CLOUDINARY_UNSIGNED_UPLOAD_PRESET);
formData.append('cloud_name', CLOUDINARY_CLOUD_NAME);
console.debug(`Uploading file (id: ${id}) to Cloudinary`, CLOUDINARY_UPLOAD_URL, formData);
const response = await fetch(CLOUDINARY_UPLOAD_URL, {
method: 'POST',
body: formData,
});
I tried different approaches, but it seems the Cloudinary API is really sensitive about it, and doesn't allow sending data in any other format other than multipart/form-data.
Cloudinary API supports not only multipart/form-data, but also JSON and application/x-www-form-urlencoded.
If you're sending a string as the value of the file parameter, such as a remote URL (in your case) or Base64, then you can use any of the above-mentioned Content-Types.
For example, you could try the following example which will successfully upload the image into a cloud (you'll want to replace <your-cloud>, <file-url> and <upload-preset> with your actual values):
fetch('https://api.cloudinary.com/v1_1/<your-cloud>/image/upload', {
method: 'POST',
headers:{
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
'file': '<file-url>',
'upload_preset': '<upload-preset>'
})
});
Note that the cloud_name is derived from the URL you're sending the data to therefore it should not be sent in your request body.

Sending a csv from front-end to backend using axios

I am trying to send a csv file from my front end in reactjs to my back end in nodejs using axios. This csv file is selected via a browse UI button and stored in state using react. After searching online this is my code for the HTTPS request:
let formmData = new FormData();
formData.append('file', this.state.selectedFile);
const {data : QueryResult} = await axios({
method: 'post',
url: "https://localhost:...",
formData,
});
Note: this.state.selectedFile contains an object which when logged on console is:
File {name: "MyFile.csv", lastModified: 1614958303483, …}
I also would like to say that this endpoint works fine when using Postman as seen below:
After printing the request in my back-end in the case this request from my frontend I see:
body: {}
I don't understand what I am doing wrong and this passes nothing to my back-end.
I also saw that this was an open issue with axios on GitHub in the past.
Can this be achieved now with axios or should i use another module? Any ideas?
You need to do:
await axios({
method: 'post',
url: "https://localhost:...",
data: formData
});
because when you write just "formData", it becomes : { formData: formData } as per (new ES2015 object shorthand property name).
You can also use:
await axios.post('your_url', formData)

Sending FormData not working in React-Native

I tried to communicate with an API to send a picture from an Iphone. I send form data by using Postman (key: mainPicture, value 'the file I choosed') and it's working well.
The problem come when I try to do it in js: the API don't find the file in my request.
Here is my code:
const formData = new FormData();
formData.append('mainPicture', {
uri: mainPicture.uri,
type: 'image/jpg',
name: `${mainPicture.id}.jpg`,
});
// ...
// api come from apisauce package
api.post(API_RES_IMG, formData, { headers: { ['Content-Type']: 'multipart/form-data' } })
Server say: "Error: Multipart: Boundary not found"
Without the content-type the server receive:
{"_parts":{"0":{"0":"mainPicture","1":{"uri":"assets-library://asset/asset.JPG?id=DE830A54-2526-44CB-81FE-00786D152080&ext=JPG","type":"image/jpg","name":"1e23519b-2006-4676-b63c-624e8198e17b.jpg"}}}}
Too much struggle from a simple thing.
Couple of issues I see here:
1) When using the fetch API, we don't need to set the Content-Type header, as fetch will take care of that, plus it will also take care of filling in the boundaries. I am assuming that this is the same with apisauce, however I might be wrong.
2) type: 'image/jpg' is not a valid mime type. It should be type: 'image/jpeg'.
you should add the content type
'Content-Type': type === 'form' ? 'multipart/form-data' : 'application/json',

Post Content-Type undefined and application/json at the same time

I am trying to upload file and post some data at the same time. The value of topicSelected is java script object array. When I check in the c# controller the value of topicSelected is null. When I check the value in my service everything is OK. I am thinking that the problem is that I am not specifying the application/json type.
setNewVideoRecord = function(file, videoName, videoVersion, topicSelected) {
console.log(topicSelected);
var self = this;
var formData = new FormData();
formData.append('file', file);
formData.append('videoName', videoName);
formData.append('videoVersion', videoVersion);
formData.append('topicSelected', topicSelected);
$http.post(self.baseUrl + "Admin/uploadVideoFile", formData, {
withCredentials: true,
headers: {
'Content-Type': undefined
},
transformRequest: angular.identity
}).then(function onSuccess(response) {
self.fileNameUpload = null;
})
}
You don't have an undefined content type. Telling your library that Content-Type is undefined just stops it trying to set its own default and let the underlying XMLHttpRequest object figure it out for itself. Since you are passing it a FormData object, it will examine that to identify the correct content-type (which is be multipart/mixed).
This will encode each piece of data (including the file you are uploading) as a separate part, each of which will have its own content-type set automatically.
You can't make XHR encode the non-file data as JSON. It will use the standard multipart format with a separate part for each of the non-file inputs (and another part for the file).
You need to write the server side code to expect that format and not JSON.
Since topicSelected is a complex data structure (and not a string) you can run it through JSON.stringify() before appending it to the form data object.
Once you read the topicSelected string, you can parse it from JSON on the server.

AngularJS $http POST request with JSON parameter AND query string

As the title suggests, I am attempting to write an AJAX call using Angular's $http object. I would like to send a post request that submits JSON in the body of the request, but also has a query string appended to the URL. After reading the documentation for $http all the way through, surfing for answers on SO, and attempting several different configurations, I am still not clear on how to pass the parameters correctly. Currently, my API responds with either empty JSON or Bad Request (400).
Code
function inviteStaff (json) {
authToken = service.getAuthToken();
return $http({
method: 'POST',
url: baseUrl + '/invitations' + '?authToken=' + authToken,
data: JSON.stringify(json),
headers: {
'Content-type': 'application/json'
}
});
}
Consuming Function
self.invite = function ($form) {
self.showLoader = true;
InvitesService.inviteStaff($scope.inviteModel).then(function () {
$form.$setPristine();
$scope.inviteModel.timestamp = new Date();
$scope.invites.unshift($scope.inviteModel);
$scope.inviteModel = self.createNewInvite();
self.showLoader = false;
},
function () {
self.showLoader = false;
});
};
Request log
data: "authToken=********&t=*****" // this is weird because it adds another "t" parameter to this query string here
headers: Object
Accept: "application/json"
Accept-Language: "en"
Content-type: "application/json"
authToken: "*********" // just the token here
__proto__: Object
method: "POST"
params: Object
timeout: Object
transformRequest: Array[1]
transformResponse: Array[1]
url: "http://****.****.com:****/doctors/invitations?authToken=*****"
__proto__: Object
Param JSON object
{
accountType: "LEAD_DOCTOR",
firstName: "kraken",
lastName: "lastName",
email: "kraken#mail.com"
}
Can anyone shed a little light on how this should work? Do I pass the json object to data or params? Do I need to use JSON.stringify on the data field? In some cases, I also saw people passing an empty string to data.
I also have been running it through Charles Proxy. I can get very close to what I want, but for some reason the JSON is showing under the JSON Text filter as a query string, and not the JSON filter.
Any ideas would be helpful. I will be refactoring and upgrading it all to use the $resource tool, but we are on a tight deadline and I just want to get this working for now. Let me know if you need more info.
UPDATE
We're using swagger to document our API, and you can actually hit the endpoint in their UI. It produces the following CURL command. I thought it might help spot the issue.
curl -X POST --header "Content-Type: application/json" --header "Accept: application/json" -d "{
\"firstName\": \"string\",
\"lastName\": \"string\",
\"email\": \"blah#mail.com\",
\"accountType\": \"CLIENT\"
}" "http://*****.****.com:****/doctors/invitations?authToken=***obfuscatedAuthToken***"
I would recommend you to take advantage of the header field:
function inviteStaff (json) {
authToken = service.getAuthToken();
return $http({
method: 'POST',
url: baseUrl + '/invitations',
data: JSON.stringify(json),
headers: {
'Content-type': 'application/json',
'authToken': authToken
}
});
}
Then on the server side, ask the current request for the header value of authToken.
Passing Tokens in the URL has some security concerns, for your reading: https URL with token parameter : how secure is it?
It turns out we had an HTTP Interceptor function elsewhere in the codebase that was changing the content-type! Wrapped that in a conditional, and it is good to go now. Thanks to everyone who helped me ensure my code was written correctly!

Categories