I was trying to upload file in react, but failed because there was bad request.
Here are some codes.
uploadDoc = (e) => {
e.preventDefault();
let file = e.target.files[0];
const url = 'https://api.agentcloud.com/api/leadfile/';
const formData = new FormData();
formData.append('lead', this.props.match.params.id);
formData.append('file',file);
const config = {
headers: {
'content-type': 'multipart/form-data',
'Authorization': 'Token 90406829e9d311b117bb7e1484a81e7b7f9f4551'
}
}
return post(url, formData, config);
}
Remove the content-type header, its incomplete.
When you use a form data object for ajax a proper content-type header is generated for you.¹
¹. I don't know react so this might only be part of the problem.
Related
I am uploading file in react using axios.
when I am doing
alert(values.attachedFile[0]);
but when I am sending values.attachedFile[0] in axios post request enpty thing is going.
const { result } = await axios.post(app.resourceServerUrl + '/file/upload', {
data: values.attachedFile[0],
headers: {
'Content-Type': 'multipart/form-data',
},
});
but as part of request is is going empty.
what mistake I am doing?
To upload file with axios you need to use FormData:
const formData = new FormData();
// ...
formData.append("data", values.attachedFile[0]);
axios.post(app.resourceServerUrl + '/file/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
Using raw HTML when I post a file to a flask server using the following I can access files from the flask request global:
<form id="uploadForm" action='upload_file' role="form" method="post" enctype=multipart/form-data>
<input type="file" id="file" name="file">
<input type=submit value=Upload>
</form>
In flask:
def post(self):
if 'file' in request.files:
....
When I try to do the same with Axios the flask request global is empty:
<form id="uploadForm" enctype="multipart/form-data" v-on:change="uploadFile">
<input type="file" id="file" name="file">
</form>
uploadFile: function (event) {
const file = event.target.files[0]
axios.post('upload_file', file, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
If I use the same uploadFile function above but remove the headers json from the axios.post method I get in the form key of my flask request object a csv list of string values (file is a .csv).
How can I get a file object sent via axios?
Add the file to a formData object, and set the Content-Type header to multipart/form-data.
var formData = new FormData();
var imagefile = document.querySelector('#file');
formData.append("image", imagefile.files[0]);
axios.post('upload_file', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
Sample application using Vue. Requires a backend server running on localhost to process the request:
var app = new Vue({
el: "#app",
data: {
file: ''
},
methods: {
submitFile() {
let formData = new FormData();
formData.append('file', this.file);
console.log('>> formData >> ', formData);
// You should have a server side REST API
axios.post('http://localhost:8080/restapi/fileupload',
formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(function () {
console.log('SUCCESS!!');
})
.catch(function () {
console.log('FAILURE!!');
});
},
handleFileUpload() {
this.file = this.$refs.file.files[0];
console.log('>>>> 1st element in files array >>>> ', this.file);
}
}
});
https://codepen.io/pmarimuthu/pen/MqqaOE
If you don't want to use a FormData object (e.g. your API takes specific content-type signatures and multipart/formdata isn't one of them) then you can do this instead:
uploadFile: function (event) {
const file = event.target.files[0]
axios.post('upload_file', file, {
headers: {
'Content-Type': file.type
}
})
}
Sharing my experience with React & HTML input
Define input field
<input type="file" onChange={onChange} accept ="image/*"/>
Define onChange listener
const onChange = (e) => {
let url = "https://<server-url>/api/upload";
let file = e.target.files[0];
uploadFile(url, file);
};
const uploadFile = (url, file) => {
let formData = new FormData();
formData.append("file", file);
axios.post(url, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
}).then((response) => {
fnSuccess(response);
}).catch((error) => {
fnFail(error);
});
};
const fnSuccess = (response) => {
//Add success handling
};
const fnFail = (error) => {
//Add failed handling
};
This works for me, I hope helps to someone.
var frm = $('#frm');
let formData = new FormData(frm[0]);
axios.post('your-url', formData)
.then(res => {
console.log({res});
}).catch(err => {
console.error({err});
});
this is my way:
var formData = new FormData(formElement);
// formData.append("image", imgFile.files[0]);
const res = await axios.post(
"link-handle",
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
}
);
How to post file using an object in memory (like a JSON object):
import axios from 'axios';
import * as FormData from 'form-data'
async function sendData(jsonData){
// const payload = JSON.stringify({ hello: 'world'});
const payload = JSON.stringify(jsonData);
const bufferObject = Buffer.from(payload, 'utf-8');
const file = new FormData();
file.append('upload_file', bufferObject, "b.json");
const response = await axios.post(
lovelyURL,
file,
headers: file.getHeaders()
).toPromise();
console.log(response?.data);
}
There is an issue with Axios version 0.25.0 > to 0.27.2 where FormData object in a PUT request is not handled correctly if you have appended more than one field but is fine with one field containing a file, POST works fine.
Also Axios 0.25.0+ automatically sets the correct headers so there is no need to specify Content-Type.
For me the error was the actual parameter name in my controller... Took me a while to figure out, perhaps it will help someone. Im using Next.js / .Net 6
Client:
export const test = async (event: any) => {
const token = useAuthStore.getState().token;
console.log(event + 'the event')
if (token) {
const formData = new FormData();
formData.append("img", event);
const res = await axios.post(baseUrl + '/products/uploadproductimage', formData, {
headers: {
'Authorization': `bearer ${token}`
}
})
return res
}
return null
}
Server:
[HttpPost("uploadproductimage")]
public async Task<ActionResult> UploadProductImage([FromForm] IFormFile image)
{
return Ok();
}
Error here because server is expecting param "image" and not "img:
formData.append("img", event);
public async Task<ActionResult> UploadProductImage([FromForm] IFormFile image)
I'm getting this error when trying to do a POST request using axios:
Error: Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream at createError
Here's my request:
async function fetchAndHandleErrors() {
const url = `/claim/${claimId}`;
const headers = {
Accept: 'application/json',
Authorization: `Bearer ${token}`,
};
const body = new FormData();
body.append('damage_description', damageDescription);
body.append('damaged_phone', {
uri: imageUri,
type: 'image/jpeg', // or photo.type
name: imageUri,
});
const result = await axios({
'post',
url: `${baseUrl}${url}`,
data: body,
headers,
});
return result.data;
}
I tried removing result.data and still get the same error. Why is that?
If you eventually still need a solution for this, I managed to get rid of this error by using the formData.pipe() method. For your case, it could look like this :
import axios from 'axios'
import concat from 'concat-stream'
import fs from 'fs'
import FormData from 'form-data'
async function fetchAndHandleErrors() {
const file = fs.createReadStream(imageUri)
let body = new FormData();
body.append('damage_description', damageDescription);
body.append('damaged_phone', file);
body.pipe(concat(data => {
const url = `/claim/${claimId}`;
const headers = {
'Authorization': `Bearer ${token}`,
...body.getHeaders()
};
const result = await axios({
'post',
url: `${baseUrl}${url}`,
data: body,
headers,
});
return result.data;
}))
}
Please let me know if you still encounters your issue, I'll be glad to help !
I need to upload file to server using fetch() from react native app
I have the following code in Angular which uses ng-file-upload:
in this function file variable is attached FormData
function addDocumentToMessage(messageId, file) {
data.filepath = file;
data.name = file.name;
return Upload.upload({
url: BackendUrl + "/messages/" + messageId + "/attachments/",
file: file,
data: data
})
.then(responseHandler)
.catch(errorHandler);
}
I tried to do following using fetch() but it doesn't work correctly: file is added to server but attachment and other fields are not saved there. Here is the code I tried:
document = { formData, name }
export const addDocumentToMessage = (token, logId, document) => {
const file = document.formData
const data = { filepath: file, name: document.name }
fetch(`${API_URL}/messages/${logId}/attachments/`, {
method: 'POST',
headers: { 'Authorization': `token ${token}`, 'Content-Type': 'multipart/form-data', Accept: 'application/json' },
body: JSON.stringify({ file: file, data: data })
})
.then(response => console.log(response.data))
.catch(error => console.log(error.message))
}
It seems that two Content-Types were mixed here:
multipart/form-data for sending binary content of the file in file
application/json for sending the some JSON data in body
Since HTTP requests only support one body having one Content-Type encoding we have to unify all of that to be multipart/form-data. The following example is using variable formData to combine (binary) file data with arbitrary JSON data.
export const addDocumentToMessage = (token, logId, document) => {
// commented these lines since I wanted to be more specific
// concerning the origin and types of data
// ---
// const file = document.formData
// const data = { filepath: file, name: document.name }
const fileField = document.querySelector("input[type='file']");
let formData = new FormData();
formData.append('file', fileField.files[0]);
formData.append('name'. fileField.files[0].name);
formData.append('arbitrary', JSON.stringify({hello: 'world'}));
fetch(`${API_URL}/messages/${logId}/attachments/`, {
method: 'POST',
headers: {
'Authorization': `token ${token}`,
'Accept': 'application/json'
// 'Content-Type': 'multipart/form-data',
},
body: formData
})
.then(response => console.log(response.data))
.catch(error => console.log(error.message))
}
The payload of HTTP request body would then look like this:
------WebKitFormBoundarypRlCB48zYzqAdHb8
Content-Disposition: form-data; name="file"; filename="some-image-file.png"
Content-Type: image/png
... (binary data) ...
------WebKitFormBoundarypRlCB48zYzqAdHb8
Content-Disposition: form-data; name="name"
some-image-file.png
------WebKitFormBoundarypRlCB48zYzqAdHb8
Content-Disposition: form-data; name="arbitrary"
{"hello":"world"}
------WebKitFormBoundarypRlCB48zYzqAdHb8--
References:
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Uploading_a_file
https://developer.mozilla.org/de/docs/Web/API/FormData/append#Syntax
I'm trying to upload a file with the Google Drive api, and I have the metadata correct, and I want to ensure that the actual file contents make it there. I have a simple page setup that looks like this:
<div id="upload">
<h6>File Upload Operations</h6>
<input type="file" placeholder='file' name='fileToUpload'>
<button id='uploadFile'>Upload File</button>
</div>
and I have a the javascript setup where the user is prompted to sign in first, and then they can upload a file. Here's the code: (currently only uploads the file metadata....)
let uploadButton = document.getElementById('uploadFile');
uploadButton.onclick = uploadFile;
const uploadFile = () => {
let ftu = document.getElementsByName('fileToUpload')[0].files[0];
console.dir(ftu);
gapi.client.drive.files.create({
'content-type': 'application/json;charset=utf-8',
uploadType: 'multipart',
name: ftu.name,
mimeType: ftu.type,
fields: 'id, name, kind'
}).then(response => {
console.dir(response);
console.log(`File: ${ftu.name} with MimeType of: ${ftu.type}`);
//Need code to upload the file contents......
});
};
First, I'm more familiar with the back end, so getting the file in bits from the <input type='file'> tag is a bit nebulous for me. On the bright side, the metadata is there. How can I get the file contents up to the api?
So According to some resources I've found in my three day search to get this going, the file simply cannot be uploaded via the gapi client. It must be uploaded through a true REST HTTP call. So let's use fetch!
const uploadFile = () => {
//initialize file data from the dom
let ftu = document.getElementsByName('fileToUpload')[0].files[0];
let file = new Blob([ftu]);
//this is to ensure the file is in a format that can be understood by the API
gapi.client.drive.files.create({
'content-type': 'application/json',
uploadType: 'multipart',
name: ftu.name,
mimeType: ftu.type,
fields: 'id, name, kind, size'
}).then(apiResponse => {
fetch(`https://www.googleapis.com/upload/drive/v3/files/${response.result.id}`, {
method: 'PATCH',
headers: new Headers({
'Authorization': `Bearer ${gapi.client.getToken().access_token}`,
'Content-Type': ftu.type
}),
body: file
}).then(res => console.log(res));
}
The Authorization Header is assigned from calling the gapi.client.getToken().access_token function, and basically this takes the empty object from the response on the gapi call and calls the fetch api to upload the actual bits of the file!
In your situation, when you upload a file using gapi.client.drive.files.create(), the empty file which has the uploaded metadata is created. If my understanding is correct, how about this workaround? I have experienced the same situation with you. At that time, I used this workaround.
Modification points:
Retrieve access token using gapi.
File is uploaded using XMLHttpRequest.
Modified script:
Please modify the script in uploadFile().
let ftu = document.getElementsByName('fileToUpload')[0].files[0];
var metadata = {
'name': ftu.name,
'mimeType': ftu.type,
};
var accessToken = gapi.auth.getToken().access_token; // Here gapi is used for retrieving the access token.
var form = new FormData();
form.append('metadata', new Blob([JSON.stringify(metadata)], {type: 'application/json'}));
form.append('file', ftu);
var xhr = new XMLHttpRequest();
xhr.open('post', 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id,name,kind');
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
xhr.responseType = 'json';
xhr.onload = () => {
console.log(xhr.response);
};
xhr.send(form);
Note:
In this modified script, it supposes that Drive API is enabled at API console and the access token can be used for uploading file.
About fields, you are using id,name,kind. So this sample also uses them.
Reference:
gapi
If I misunderstand your question or this workaround was not useful for your situation, I'm sorry.
Edit:
When you want to use fetch, how about this sample script?
let ftu = document.getElementsByName('fileToUpload')[0].files[0];
var metadata = {
'name': ftu.name,
'mimeType': ftu.type,
};
var accessToken = gapi.auth.getToken().access_token; // Here gapi is used for retrieving the access token.
var form = new FormData();
form.append('metadata', new Blob([JSON.stringify(metadata)], {type: 'application/json'}));
form.append('file', ftu);
fetch('https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id,name,kind', {
method: 'POST',
headers: new Headers({'Authorization': 'Bearer ' + accessToken}),
body: form
}).then((res) => {
return res.json();
}).then(function(val) {
console.log(val);
});
With https://www.npmjs.com/package/#types/gapi.client.drive
const makeUploadUrl = (fileId: string, params: Record<string, boolean>) => {
const uploadUrl = new URL(
`https://www.googleapis.com/upload/drive/v3/files/${fileId}`
)
Object.entries({
...params,
uploadType: 'media',
}).map(([key, value]) => uploadUrl.searchParams.append(key, `${value}`))
return uploadUrl
}
const uploadDriveFile = async ({ file }: { file: File }) => {
const params = {
enforceSingleParent: true,
supportsAllDrives: true,
}
// create file handle
const { result } = await gapi.client.drive.files.create(params, {
// CAN'T have the upload type here!
name: file.name,
mimeType: file.type,
// any resource params you need...
driveId: process.env.DRIVE_ID,
parents: [process.env.FOLDER_ID],
})
// post the file data
await fetch(makeUploadUrl(result.id!, params), {
method: 'PATCH',
headers: new Headers({
Authorization: `Bearer ${gapi.client.getToken().access_token}`,
'Content-Type': file.type,
}),
body: file,
})
return result
})
}