File upload using axios post with basic auth - javascript

I want to upload file to server using axios post request.
I'm using the input tag for file and onChange setting the state.
If I use headers: { "content-type": "multipart/form-data" }, in axios the code gives error 400.
And If I remove it the code works fine but sends empty array via POST.
import React, { Component } from "react";
import axios from "axios";
export default class Image extends Component {
state = {
image: null
};
handleFiles = e => {
this.setState({ image: e.target.files[0] });
};
handleUpload = () => {
var session_url = "https:/localhost:3000/wp-json/gf/v2/entries/";
const fd = new FormData();
fd.append("image", this.state.image);
var entries = {
form_id: "1",
15: fd
};
axios
.post(session_url, entries, {
headers: { "content-type": "multipart/form-data" },
withCredentials: true,
auth: {
username: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
password: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
})
.then(res => {
this.setState({ data: res.data });
console.log(res, "Authenticated");
})
.catch(function(error) {
console.log("Error on Authentication", error.message);
});
};
render() {
return (
<div>
<input type="file" onChange={this.handleFiles} />
<button onClick={this.handleUpload}>Upload</button>
</div>
);
}
}

Your data is broken
Pass Axios either a plain object or a FormData object. It can handle both.
It will not properly serialise a plain object where one of the property values is a FormData object.
const entries = new FormData();
fd.append("image", this.state.image);
fd.append("form_id": "1");
Your content type is broken
Do not manually set a Content-Type header. It will be generated (including the mandatory boundary parameter for multipart requests) from the FormData object.
Your server-side code might not support this
You need to ensure that your server-side code can handle a multi-part request.

Related

How to create javascript Request Object with Body type as form-data file

I am trying to code POST REST API request as shown in the above Postman screenshot.
Here is my code:
import { fromFetch } from 'rxjs/fetch';
import { switchMap } from 'rxjs/operators';
interface FetchRequest<TBody> {
method?: FetchMethod;
body?: TBody;
headers?: FetchHeaders;
isBlobRequest?: boolean;
}
export const request = <TBody extends BodyInit = BodyInit>(
url: string,
fetchRequest?: FetchRequest<TBody>,
contentType: string
) => {
const { method = 'POST', body, headers } = fetchRequest ?? {};
const { dispatch } = createStore();
let request = new Request(`${domainUrl}${url}`, {
method,
body: body ? body : null,
headers: {
...headers,
'Content-type': contentType ,
},
credentials: 'include',
});
return fromFetch(request).pipe(
switchMap((response: Response) => {
if (response.status === SUCCESS_CODE) {
if (isBlobRequest) {
return response.blob();
}
return response.text();
} else if (response.status === USER_SESSION_EXPIRED_CODE) {
dispatch(authAsyncActions.setSessionExpired());
throw response;
} else {
// This triggers error-handling and allows us to inspect the entire
// error response including its status and body
throw response;
}
})
);
};
const callUploadAPI = (file) => {
let formData = new FormData();
formData.append('file', file);
request(`urlUploadFile`, { method: 'POST', body: formData}, 'application/vnd.ms-excel')
}
In above code I am using fromFetch of "rxjs/fetch" to call the POST REST API and passing "Request" object in fromFetch().
Request interface is inside typescript as per the below screenshot.
Backend is Python flask server and on the backend side in Python code I am using file = request.files['file'] to get the file which is working when I call the API through Postman but when I call the API through frontend code it is not getting file.
How can I set the Request "Body" type as "form-data" and "KEY" type as File in frontend code?
You're explicitly setting the Content-type to application/vnd.ms-excel, but you don't need to set this header
As far as I know, if the body of a fetch request is a FormData object, the browser automatically sets the content type header to multipart/form-data

react-native app and RapidAPI - AI Picture Colorizer

I'm working through this Udemy react-native course and finding it to be a bit outdated.
The course section 9 works with the RapidAPI AI Picture Colorizer to select a base64 encoded image. But it looks like the API was updated to no longer use base64 and instead uses a image upload.
I'm using react-native-imagepicker and I'm not sure how to update the code to pick an image from the device's library and upload the way the RapidAPI documentation reads.
This is the RapidAPI example code:
import axios from "axios";
const form = new FormData();
form.append("image", "");
form.append("renderFactor", "25");
const options = {
method: 'POST',
url: 'https://ai-picture-colorizer.p.rapidapi.com/colorize-api-bin-mjbcrab',
headers: {
'content-type': 'multipart/form-data; boundary=---011000010111000001101001',
'x-rapidapi-host': 'ai-picture-colorizer.p.rapidapi.com',
'x-rapidapi-key': '[MY-API-KEY]'
},
data: '[form]'
};
axios.request(options).then(function (response) {
console.log(response.data);
}).catch(function (error) {
console.error(error);
});
This is my code:
import React, {Component} from 'react';
import {View, Text, Image, StyleSheet} from 'react-native';
import {Button} from 'react-native-elements';
import ProgressBar from 'react-native-progress/Bar';
import {launchImageLibrary} from 'react-native-image-picker';
import axios from 'axios';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
menu: true,
dataSource: null,
loading: true,
base64: null,
fileName: null,
};
}
selectGallaryImage() {
const options = {
includeBase64: true,
};
launchImageLibrary(options, response => {
if (response.didCancel) {
console.log('User canceled Image');
} else if (response.error) {
console.log('Image picker error');
} else if (response.customButton) {
console.log('User pressed custom Button');
} else {
this.setState(
{
base64: response.assets[0].base64,
fileName: response.assets[0].fileName,
},
() => console.log('base64 fileName ', this.state.fileName),
);
this.goForAxios();
}
});
}
goForAxios() {
const {fileName} = this.state;
const form = new FormData();
form.append('image', fileName);
form.append('renderFactor', '10');
this.setState({menu: false});
console.log('Starting request');
axios
.request({
method: 'POST',
url: 'https://ai-picture-colorizer.p.rapidapi.com/colorize-api-bin-mjbcrab',
headers: {
'content-type':
'multipart/form-data; boundary=---011000010111000001101001',
'x-rapidapi-host': 'ai-picture-colorizer.p.rapidapi.com',
'x-rapidapi-key': '[MY-API-KEY]',
},
data: '[form]',
})
.then(response => {
this.setState({
loading: false,
dataSource: response.data,
});
})
.catch(error => {
console.error(error);
});
}
...
}
I've reached out to the API author and they suggested I refer to the RapidAPI documentation, but I can't seem to sort it out.
I keep receiving [Error: Request failed with status code 500] and running the Test Endpoint in RapidAPI returns OK but with "There's no example response for this endpoint."
Thanks for the help.
The server usually returns a 500 status code when it cannot find any suitable status code due to unexpected situations. I'm not a React Native expert, but here to help.
You're using react-native-image-picker, it already returns the Base64 value in its response. So instead of:
base64: response.assets[0].base64,
Try this
base64: response.data,
If it still does not work, you can use another API that provides similar functionality. There are thousands of APIs you can find on RapidAPI Hub belonging to the same category. For example, use this Image Colorization API. It lets you pass the image as the URL.

How to post image and array together with react native to backend?

I am trying to send an image file and data (object or array) to a backend with react native. I tried using fetch and the code below and got it to send an image to backend. But my target is to send the image and data together. How can I achieve this?
uploadPhoto = async (response) => {
const formData = new FormData();
formData.append('fileToUpload', {
uri: response.path,
name: 'image',
type: response.mime,
imgPath: response.path
});
const infos={ad:'onur',soyad:'cicek'};
try {
const rest = await fetch('https://www.birdpx.com/mobile/fotografyukle/'+this.state.user.Id, {
method: 'POST',
headers: { 'Accept': 'application/json', 'Content-type': 'multipart/form-data' },
body: formData
});
const gelenVeri = await rest.text();
let convertedVeri=JSON.parse(gelenVeri);
console.log(convertedVeri);
return false
} catch (err) {
console.log(err)
}
};
I need to post const infos={ad:'onur',soyad:'cicek'}; and the image.
You can pass it in formData itself
formData.append('other_infos', infos);
Here other_infos will be key that required to extract from backend, you can append n number of parameters in FormData
You can do it by adding other data to the FormData.
like in the example
formData.append('infos', infos);

React-Redux: PATCH with params

I'm trying to do a patch to modify some informations using a form.
I take the new data, if they are modify and then create a new FormData. Then I pass the id and the formdata to do a patch. But something in the patch goes wrong.
I have a page in which I take the previous information( I pass these informations using react-router-dom and have a form to modify the data.
FormPage.js
When the submit button is press I call this:
class MeetingModify extends Components{
//the state taken from the previous page
handleSubmit(event){
event.preventDefault();
const idmeet = this.state.meet.id
var newMeet = new FormData();
if(this.state.information != this.props.location.state.information){
newMeet.append("information", this.state.information);
}
if(this.state.date != this.props.location.state.date){
newMeet.append("date", this.state.date);
}
if(this.state.hours != this.props.location.state.hours){
newMeet.append("hours", this.state.hours);
}
this.props.changeMeeting(idmeet, newMeet);
}
render(){
return(
//..form..
)
function mapDispatchToProps(dispatch) {
return {
changeMeeting: (idmeet, newMeet) => dispatch(changeMeeting(idmeeting, newMeet)),
};
}
export default connect(null, mapDispatchToProps)(MeetingModify);
in the Actions.js
export const changeMeeting = (idmeet, newMeet) => {
return (dispatch) => {
fetch(meeturl + '/' + idmeet , {
method: "PATCH",
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: newMeet,
})
.then(
(response) => {return response.json()},
(error) => console.log("An error occurred.", error)
)
.then((newMeet) => {
dispatch({
type: "MEETING_PATCH",
newMeet: newMeet
});
});
}
How can I do to execute the patch ?? Thank you
If you need more details I can edit :)
EDIT. I don't understand why it gives me a CORS error ( I have resolved it, infact for the other method like post or get it works. )
I have tried to console log the body: newMeet and if I use form data is like:
["information", "new information"]
I tried to the same with Postman (or cocoaRestClient) and the curl is:
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'information=new information'
I see on postman the javascript - fetch:
var urlencoded = new URLSearchParams();
urlencoded.append("information", "New Information");
var requestOptions = {
method: 'PATCH',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
};
It's the same to mine...

How to send data correct axios Error: Multipart: Boundary not found

I don't know why I receive on server [Error: Multipart: Boundary not found]
and bundle.js:37628 POST http://localhost:8800/exporttocsv 500 (Internal Server Error)
When I make post through
<form action="/exporttocsv" method="POST" encType="multipart/form-data">
post works correctly, but through axios doesn't work.
Please help me fix the mistake
this my code
/--client
import axios from 'axios'
var formData = new FormData()
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
export const ipmortToCSV = (file) => dispatch => {
formData.append('file',file)
console.log(formData.getAll('data'))
axios.post('/exporttocsv', {
"UploadCommand": formData
},config)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
//--server
const router = require('express').Router()
var csv = require('csv-express')
const controllers = require('../../controllers/exporttocsv')
var multer = require('multer')
var upload = multer({dest : 'exporttocsv/'})
router.get('/', (req, res) => {
controllers.exportToCsv(req,res)
})
router.post('/',upload.single('file'),(req,res) => {
//controllers.importToCsv(req,res)
})
module.exports = router
You can do this ...
Instantiate a new FormData instance.
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
let fd = new FormData();
fd.append('file',files[0])
return axios.post("http://localhost:5000/upload", fd, config)
Usingconcat and concat-stream
const concat = require("concat-stream")
const fd = new FormData()
fd.append("hello", "world")
fd.append("file", fs.createReadStream(file))
fd.pipe(concat(data => {
axios.post("/hello", data, {
headers: fd.getHeaders()
})
}))
Using promise
const promise = new Promise((resolve) => {
const fd = new FormData();
fd.append("hello", "world");
fd.append("file", fs.createReadStream(binaryFile));
fd.pipe(concat({ encoding: 'buffer' }, data => resolve({ data, headers: fd.getHeaders() })));
});
promise.then(({ data, headers }) => axios.post('/hello', data, { headers }));
I hope I've been useful! :)
References:
github.com - Can't get a .post with Content-Type...
github.com - Better solution using axios, form-data, fs
https://stackoverflow.com/a/47630754/3332734
I was struggling with this issue of multipart boundary not found with fetch api calling to a nestjs server. What I tried was to remove the
'Content-Type': 'multipart/form-data',
headers so that Fetch api automatically set the headers and it worked. Try it out
By default axios do not attach boundary to content type header. You have to do it manually:
axios.post(`${this.baseUrl}/${path}`, formData, {
headers: {
'Content-Type': `multipart/form-data; boundary=${formData.getBoundary()}`,
},
})
It is especially important if you talking to spring server.
In other case you will see exception:
org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
I was getting this problem with Axios via JavaScript because the content-type header was multipart-form-data but the boundary was missing.
Based on my research, a good way to handle it is to allow Axios to auto-detect the content type and set the headers correctly itself.
Here is an idea for how to accomplish this:
const formDataWithFiles = hasFiles ? new FormData() : undefined;
if (formDataWithFiles) {
// axios will automatically set the content-type to multipart/form-data if the
// data param is a FormData object
// otherwise, it will use application/json
// (study the Dev Tools > Network tab > XHR tab headers)
Object.keys(modifiedFields)
.forEach(field => formDataWithFiles.append(field, modifiedFields[field]));
}
const { data } = await axios({
method,
url: actionUrl,
data: hasFiles ? formDataWithFiles : modifiedFields,
headers: {
...axios.defaults.headers,
...headers,
},
});
return data;
The above code is in a generic handleSubmit function that can be called from anywhere in the client-side.
Here is the function signature:
const { data } = await this.submitForm({
actionUrl: this.actionUrl,
method: this.method,
modifiedFields: {
...this.modifiedUser,
},
hasFiles: true,
});
In the above code, there are two use cases. The first is the default case, where a normal payload is sent via a flat object. The second is the case when the form has files and you want multipart/form-data. In this case, we use the FormData Object as a vessel to instruct Axios to auto-detect the necessary headers and set the correct boundary.
If you do not specify the headers correctly, it is possible to receive an empty $request->all() Array in Laravel, or perhaps any server such as node.js.
The short answer to my answer is to use the FormData Object because it contains more information than a plain-old-JavaScript-object. With it, you can also access:
const formData = new FormData();
console.log('boundary:', formData._boundary);
As my annotation above hints towards, use the Dev Tools > Network tab > XHR tab to examine your request headers and make sure you have content-type application/json or application/x-www-form-urlencoded for regular form submits and multipart/form-data' if you are uploading a file.
For me the main reason was what the OP did; sending the data argument of axios.post as an object ({ key: formDataObj}) instead of just formDataObj directly as the arg.
For me add the following code to fixes it.
axios.post('/xxx/Upload', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
transformRequest: (data) => {
return data
},
})
Okay, I would like to share my solution as I struggled with this problem for almost a day. The issue was caused by an incorrect package version. Around 27.0.0, some changes were made to the form data sending, which resulted in issues with the boundaries. I'm not sure what version you folks are using, but it might be worth checking if this is the cause of your problem.
https://github.com/axios/axios/issues/4631

Categories

Resources