My code isn't working as it should, it keeps receiving a 400 from the server and failing to upload. I must be making mistakes in the react component so please can you take a look for me? All's working in Postman, so the backend code seems fine.
import React, { useState } from "react";
import axios from "axios";
const UploadAvatar = () => {
const [image, setImage] = useState();
const handleUpload = async (e) => {
e.preventDefault();
const config = {
headers: {
"content-type": "multipart/form-data",
Authorization: localStorage.getItem("token"),
},
};
try {
const formData = new FormData();
formData.append("avatar", image);
const response = await axios.post(
"/users/me/avatar",
{ formData },
config
);
console.log(response);
} catch (err) {
console.error(err.message);
}
};
return (
<div>
<form onSubmit={handleUpload}>
Select image to upload:
<input
type="file"
onChange={(e) => setImage(e.target.value)}
name="fileToUpload"
/>
<input type="submit" value="Upload Image" name="submit" />
</form>
</div>
);
};
export default UploadAvatar;
There some things you need to do.
This is not related to the problem, but I think it is worth checking:
const config = {
headers: {
"content-type": "multipart/form-data",
Authorization: localStorage.getItem("token"),
},
}
What scheme is your Authorization (Basic, Bearer, OAuth)?
. If its a Bearer schema (e.g.), is your localStorage.getItem("token") returning only the token or is returning "Bearer {token}"? For bearer token, you need to include the word 'Bearer' before the token.
The content-type it's not really necessary here, but you can let it there if you prefer.
In your code, you need to do some changes:
In your handleUpload you need to do this:
try {
const formData = new FormData();
formData.append("avatar", image);
// I just removed the curly brackets from formData
const response = await api.post("/users/me/avatar", formData, config);
console.log(response);
} catch (err) {
console.error(err.message);
}
And in your input file type:
<input
type="file"
onChange={(e) => setImage(e.target.files[0])}
name="fileToUpload"
/>
For input file types, the target should be e.target.files, wich returns a list of files and each file is a FileList object. As you sending only one image you can set it as e.target.files[0] to get the first image.
And that´s all. It should work now! :)
I did a poc here and everything goes ok.
for bad request
it happens because of axios ,
your not sending json data
in your code
const response = await axios.post(
"/users/me/avatar",
{ formData },<---------here is the problem object
formData ,<-------try without curly brazes or use below detailed axios
config
);
console.log(response);
} catch (err) {
console.error(err.message);
}
};
change axios
axios({
method: 'post',
url: 'myurl',
data: formData ,
headers: {'Content-Type': 'multipart/form-data' }
})
.then(function (response) {
//handle success
console.log(response);
})
.catch(function (response) {
//handle error
console.log(response);
});
another problem
YOUR SENDING FILES
<input
type="file"
onChange={(e) => setImage(e.target.value)}<-------wrong e.target.files[0]
name="fileToUpload"
/>
change
if u sending mutliple files
e.target.files
or if you sending single file use
e.target.files[0]
change code
<input type="file"
onChange={(e) => setImage(e.target.files[0])}
name="fileToUpload"
/>
Related
I am trying to make an application that allows user to upload images and videos posts to the facebook page by uploading them to the application.
My access token and all other stuffs are working fine for uploading pictures, but not for videos.
const [file, setFile] = useState([]);
code for uploading picture (working)
const formData = new FormData();
formData.append("source", file)
axios.post("https://graph.facebook.com/<pageId>/photos?",
formData,
{
headers:
{
"Content-Type": "multipart/form-data",
"Authorization": "Bearer <access_token>"
}
}
).then(
(res) => {
if (res.data["id"]) {
alert("Upload Successfully")
}
}
).catch((err) => {
alert("Error communicating to server")
console.log(err)
})
code for uploading video (not working)
const formData = new FormData();
formData.append("file", file);
axios.post("https://graph-video.facebook.com/v16.0/<page_id>/videos",
formData,
{
headers:
{
"Content-Type": "multipart/form-data",
"Authorization": "Bearer <same_access_token>"
}
}
).then(
(res) => {
if (res.data["id"]) {
alert("Upload Successfully")
}
}
).catch((err) => {
alert("Error communicating to server")
console.log(err)
})
code for getting input
<input className="btn" type="file" accept="video/mp4,video/x-m4v,video/*"
onChange=
{
(e) => { handleChange(e) }
}
/>
<input className="btn" type="file" accept="image/png, image/jpeg"
onChange=
{
(e) => { handleChange(e) }
} />
const handleChange = (e) => {
setFile(e.target.files[0]);
}
Bit strange to get " access to fetch URL blocked by cors preflight etc..." because the PUT request works for me in postman but i only get that error when i try to upload in react js . Here is my code above.
I get another error by the way "
error TypeError: Failed to fetch
at Upload (Upload.js:28:1)
Which i have no clue why i am getting a type error . Any help would be appreciated
import axios from 'axios';
import React,{Component} from 'react';
import { useState } from 'react';
export default function Upload(){
const [data,setData] = useState()
if (data) {
const file = (data.target.files[0])
console.log(file)
const myHeaders = new Headers();
myHeaders.append("Content-Type", "image/jpeg");
const formdata = new FormData();
formdata.append('File', file);
const requestOptions = {
method: 'PUT',
headers: myHeaders,
body: formdata,
redirectXXX: 'manual'
};
fetch("https://XXXX.execute-api.us-east-1.amazonaws.com/prod/XXXX/IMG_0733.jpg", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
}
return (
<div>
<form>
<input type="file" onChange={(e) => setData(e)}/>
</form>
</div>
)
}
expecting upload to put into API same way the postman does it.,
Up to this point a file can be viewed on input:
export async function store(input) {
console.log("input", input);
return httpClient.post(`${apiEndpoint}`, input);
}
On above console.log, it shows data as:
But, on the serverside laravel, if I print_r($request->all()) it shows data as:
My http client looks like this:
import axios from "axios";
const apiURL = process.env.MIX_SPA_URL;
axios.defaults.headers.common["Content-Type"] = "application/json";
axios.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
axios.defaults.withCredentials = true;
let client = axios.create({
baseURL: apiURL,
});
axios.interceptors.response.use(null, (error) => {
const expectedError =
error.response &&
error.response.status >= 400 &&
error.response.status < 500;
if (!expectedError) {
console.log("error from httpClient >>", error);
}
return Promise.reject(error);
});
function setJwt(token) {
client.defaults.headers.common["Authorization"] = "Bearer " + token;
}
const httpClient = {
get: client.get,
post: client.post,
put: client.put,
delete: client.delete,
setJwt,
};
export default httpClient;
Also, in case if you want to look how I have created input file using react-hook-form as:
<input
className={`form-control w-full ${
errors["cover_image"] ? "border-red-500" : ""
}`}
type="file"
{...register("cover_image")}
/>
Why are the images not being sent to the server?
In case of laravel, I am using laravel sanctum in combination with fortify. And, the middleware added for this route are auth:sanctum and verified.
Also, I have tried by adding headers as: "Content-Type": "multipart/form-data",
export async function store(input) {
console.log("input", input);
return httpClient.post(`${apiEndpoint}`, input, {
headers: {
"Content-Type": "multipart/form-data",
},
});
}
But, with this header, not a single data got send to the server. Here, is the screenshot:
I think you must put your file in formData and then pass it as your post request data
export async function store(input) {
const formData = new FormData();
formData.append("cover_image", input.cover_image[0]);
formData.append("blockchain", input.blockchain);
formData.append("description", input.description);
formData.append("name", input.name);
return await httpClient.post(`${apiEndpoint}`, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
}
I am posting to my API and where it should return a response nothing is logged to the console.
I have the following code:
import React, {useState} from "react"
import axios from 'axios';
const conn = axios.create({
baseURL: 'https://myurl.com',
headers: {
'Content-Type': 'application/json'
}
});
const Login = () => {
const [un,setUn] = useState('');
const [pw,setPw] = useState('');
const pleaseLogin = (un, pw) => {
//Hard coding the values for now
const email = "me#mydomain.org";
const pass = "weakpassword";
conn.post('/apilogin/authenticate/',
{
email: email, password: pass
})
.then(res => {
console.log("This is never reached");
console.log(res);
console.log(res.data);
}, (error) => {
console.log("This is reached if I alter the URL");
console.log(error);
});
}
return (
<form onSubmit={() => pleaseLogin(un,pw)}>
<label>
User Name:
<input type="text" value={un} onChange={setUn} />
</label>
<label>
Password:
<input type="text" value={pw} onChange={setPw} />
</label>
<input type="submit" value="Submit" />
</form>
);
};
export default Login
If I change the URL then I get a 404 error so the error handling is working, but no response is logged at all if the URL is correct. If I run this call to the API through postman, then it works fine and I get a JSON response like below:
{
"refresh": "tokendata",
"access": "tokendata"
}
When trying using fetch, I was seeing CORS errors when logging the response, but since adding the cors setting it stopped, but I am still not seeing a response. There is no error on the server I can see, I just see 200OK when the request is processed.
This is using the Fetch method:
const pleaseLogin = async (un, pw) => {
const data = { email: un, password: pw };
fetch(`${baseUrl}/apilogin/authenticate/`, {
method: 'POST', // or 'PUT'
headers: {
'Content-Type': 'application/json',
},
mode: 'cors',
body: JSON.stringify(data),
})
.then(response => {console.log(response)});
}
I have tried running react from http://localhost:3000 and from http://myipaddress:3000 as well and that hasn't made a difference.
Server logs:
2020-12-03T10:04:37.365435+00:00 heroku[router]: at=info method=POST path="/apilogin/authenticate/" host=OMITTED fwd="myexternalipaddress" dyno=web.1 connect=0ms service=200ms status=200 bytes=761 protocol=https
2020-12-03T10:04:37.363496+00:00 app[web.1]: omitted - - [03/Dec/2020:10:04:37 +0000] "POST /apilogin/authenticate/ HTTP/1.1" 200 438 "http://localhost:3000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
UPDATE: modified headers below.
headers: {
'Content-Type': 'application/json',
'Sec-Fetch-Site': 'cross-site',
},
UPDATE: I have tried running the react app on a development server where the API backend runs. The django application is set up to handle CORS. I have tried with Firefox and the server receives a request like below:
OPTIONS /api/authenticate/ HTTP/1.1" 200 0
If I run the app in Chrome then it gets nothing at all. I understand that this is a pre-flight request. As far as I can tell, the server is set up to handle this. I'm not sure where to go from here. I know that other frameworks work with the same request. I have tested with static HTML as below and this works.
<script>
const login = async () => {
const email = "emailaddress";
const pass = "password";
try {
const response = await axios({
'Content-Type' : 'application/json',
url: 'http://127.0.0.1:8000/api/authenticate/',
method: 'post',
data: { email: email, password: pass },
}).then( response => {
console.log(response);
});
} catch (e) {
console.log(e);
}
}
login();
</script>
try add "proxy": "http://localhost:3000" to your package.json inside your client folder (to overcome CORS issues).:-
/client/package.json
{
"name": "proj_name",
"version": "0.1.0",
"proxy": "http://localhost:3000"
}
Then, add async await to your res (a lot of times I do stumble upon this issue & this helps me):-
Login.js:-
import React, {useState} from "react"
import axios from 'axios';
const conn = axios.create({
baseURL: 'https://myurl.com',
headers: {
'Content-Type': 'application/json'
}
});
const Login = () => {
const [un,setUn] = useState('');
const [pw,setPw] = useState('');
const sayHello = () => {
console.log("hello");
}
const pleaseLogin = (un, pw) => {
//Hard coding the values for now
const email = "me#mydomain.org";
const pass = "weakpassword";
conn.post('/apilogin/authenticate/',
{
email: email, password: pass
})
.then(async res => {
const result = await res
console.log("This is never reached");
console.log(result);
console.log(result.data);
}, (error) => {
console.log("This is reached if I alter the URL");
console.log(error);
});
}
return (
<form onSubmit={() => pleaseLogin(un,pw)}>
<label>
User Name:
<input type="text" value={un} onChange={setUn} />
</label>
<label>
Password:
<input type="text" value={pw} onChange={setPw} />
</label>
<input type="submit" value="Submit" />
</form>
);
};
export default Login
I don't quite understand when you stated it works with static HTML in your updates above.
I seems like these part of code (show below) might have contribute to your issue:-
// this part
const conn = axios.create({
baseURL: 'https://myurl.com',
headers: {
'Content-Type': 'application/json'
}
});
// and this part
conn.post('/apilogin/authenticate/',
{
email: email, password: pass
})
.then(res => {
console.log("This is never reached");
console.log(res);
console.log(res.data);
}, (error) => {
console.log("This is reached if I alter the URL");
console.log(error);
});
If you build your axios around that cause of wanting it to be dynamic. Then you could change it to your own static HTML updates (which I think can also can be dynamic with some changes):-
Login.js:-
import React, {useState} from "react"
import axios from 'axios';
const baseUrl = 'https://myurl.com'
const config = {
headers: {
'Content-Type': 'application/json'
}
}
const Login = () => {
const [un,setUn] = useState('');
const [pw,setPw] = useState('');
const sayHello = () => {
console.log("hello");
}
const pleaseLogin = async(un, pw) => {
//Hard coding the values for now
const email = "me#mydomain.org";
const pass = "weakpassword";
// the BASEURL & CONFIG can still be DYNAMIC
await axios.post(baseUrl + '/apilogin/authenticate/',
{
email: email, password: pass
}, config)
.then(async res => {
const result = await res
console.log("This is never reached");
console.log(result);
console.log(result.data);
})
.catch(error => {
console.log("This is reached if I alter the URL");
console.log(error);
});
}
return (
<form onSubmit={() => pleaseLogin(un,pw)}>
<label>
User Name:
<input type="text" value={un} onChange={setUn} />
</label>
<label>
Password:
<input type="text" value={pw} onChange={setPw} />
</label>
<input type="submit" value="Submit" />
</form>
);
};
export default Login
We want to send an image file as multipart/form to the backend, we try to use html form to get file and send the file as formData, here are the codes
export default class Task extends React.Component {
uploadAction() {
var data = new FormData();
var imagedata = document.querySelector('input[type="file"]').files[0];
data.append("data", imagedata);
fetch("http://localhost:8910/taskCreationController/createStoryTask", {
mode: 'no-cors',
method: "POST",
headers: {
"Content-Type": "multipart/form-data"
"Accept": "application/json",
"type": "formData"
},
body: data
}).then(function (res) {
if (res.ok) {
alert("Perfect! ");
} else if (res.status == 401) {
alert("Oops! ");
}
}, function (e) {
alert("Error submitting form!");
});
}
render() {
return (
<form encType="multipart/form-data" action="">
<input type="file" name="fileName" defaultValue="fileName"></input>
<input type="button" value="upload" onClick={this.uploadAction.bind(this)}></input>
</form>
)
}
}
The error in backend is "nested exception is org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found".
After reading this, we tried to set boundary to headers in fetch:
fetch("http://localhost:8910/taskCreationController/createStoryTask", {
mode: 'no-cors',
method: "POST",
headers: {
"Content-Type": "multipart/form-data; boundary=AaB03x" +
"--AaB03x" +
"Content-Disposition: file" +
"Content-Type: png" +
"Content-Transfer-Encoding: binary" +
"...data... " +
"--AaB03x--",
"Accept": "application/json",
"type": "formData"
},
body: data
}).then(function (res) {
if (res.ok) {
alert("Perfect! ");
} else if (res.status == 401) {
alert("Oops! ");
}
}, function (e) {
alert("Error submitting form!");
});
}
This time, the error in backend is: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
Do we add the multipart boundary right? Where should it be?
Maybe we are wrong at first because we don't get the multipart/form-data. How can we get it correctly?
We just try to remove our headers and it works!
fetch("http://localhost:8910/taskCreationController/createStoryTask", {
mode: 'no-cors',
method: "POST",
body: data
}).then(function (res) {
if (res.ok) {
alert("Perfect! ");
} else if (res.status == 401) {
alert("Oops! ");
}
}, function (e) {
alert("Error submitting form!");
});
Here is my solution for image upload with preview through axios.
import React, { Component } from 'react';
import axios from "axios";
React Component Class:
class FileUpload extends Component {
// API Endpoints
custom_file_upload_url = `YOUR_API_ENDPOINT_SHOULD_GOES_HERE`;
constructor(props) {
super(props);
this.state = {
image_file: null,
image_preview: '',
}
}
// Image Preview Handler
handleImagePreview = (e) => {
let image_as_base64 = URL.createObjectURL(e.target.files[0])
let image_as_files = e.target.files[0];
this.setState({
image_preview: image_as_base64,
image_file: image_as_files,
})
}
// Image/File Submit Handler
handleSubmitFile = () => {
if (this.state.image_file !== null){
let formData = new FormData();
formData.append('customFile', this.state.image_file);
// the image field name should be similar to your api endpoint field name
// in my case here the field name is customFile
axios.post(
this.custom_file_upload_url,
formData,
{
headers: {
"Authorization": "YOUR_API_AUTHORIZATION_KEY_SHOULD_GOES_HERE_IF_HAVE",
"Content-type": "multipart/form-data",
},
}
)
.then(res => {
console.log(`Success` + res.data);
})
.catch(err => {
console.log(err);
})
}
}
// render from here
render() {
return (
<div>
{/* image preview */}
<img src={this.state.image_preview} alt="image preview"/>
{/* image input field */}
<input
type="file"
onChange={this.handleImagePreview}
/>
<label>Upload file</label>
<input type="submit" onClick={this.handleSubmitFile} value="Submit"/>
</div>
);
}
}
export default FileUpload;
The file is also available in the event:
e.target.files[0]
(eliminates the need for document.querySelector('input[type="file"]').files[0];)
uploadAction(e) {
const data = new FormData();
const imagedata = e.target.files[0];
data.append('inputname', imagedata);
...
Note:
Use console.log(data.get('inputname')) for debugging, console.log(data) will not display the appended data.
https://muffinman.io/uploading-files-using-fetch-multipart-form-data/ worked best for me. Its using formData.
import React from "react";
import logo from "./logo.svg";
import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";
import Button from "react-bootstrap/Button";
const ReactDOM = require("react-dom");
export default class App extends React.Component {
constructor(props) {
super(props);
this.test = this.test.bind(this);
this.state = {
fileUploadOngoing: false
};
}
test() {
console.log(
"Test this.state.fileUploadOngoing=" + this.state.fileUploadOngoing
);
this.setState({
fileUploadOngoing: true
});
const fileInput = document.querySelector("#fileInput");
const formData = new FormData();
formData.append("file", fileInput.files[0]);
formData.append("test", "StringValueTest");
const options = {
method: "POST",
body: formData
// If you add this, upload won't work
// headers: {
// 'Content-Type': 'multipart/form-data',
// }
};
fetch("http://localhost:5000/ui/upload/file", options);
}
render() {
console.log("this.state.fileUploadOngoing=" + this.state.fileUploadOngoing);
return (
<div>
<input id="fileInput" type="file" name="file" />
<Button onClick={this.test} variant="primary">
Primary
</Button>
{this.state.fileUploadOngoing && (
<div>
<h1> File upload ongoing abc 123</h1>
{console.log(
"Why is it printing this.state.fileUploadOngoing=" +
this.state.fileUploadOngoing
)}
</div>
)}
</div>
);
}
}
React File Upload Component
import { Component } from 'react';
class Upload extends Component {
constructor() {
super();
this.state = {
image: '',
}
}
handleFileChange = e => {
this.setState({
[e.target.name]: e.target.files[0],
})
}
handleSubmit = async e => {
e.preventDefault();
const formData = new FormData();
for (let name in this.state) {
formData.append(name, this.state[name]);
}
await fetch('/api/upload', {
method: 'POST',
body: formData,
});
alert('done');
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
name="image"
type="file"
onChange={this.handleFileChange}>
</input>
<input type="submit"></input>
</form>
)
}
}
export default Upload;
the request was rejected because no multipart boundary was found".
When you send multipart/form-data, the boundary is automatically added to a content-type of a request header. you have to tell the server when the parameter ends with the boundary rule. You had to set the Content-type like this
"Content-Type": `multipart/form-data: boundary=add-random-characters`
This article with guide you: https://roytuts.com/boundary-in-multipart-form-data/
The boundary is included to separate name/value pair in the
multipart/form-data. The boundary parameter acts like a marker for
each pair of name and value in the multipart/form-data. The boundary
parameter is automatically added to the Content-Type in the http
(Hyper Text Transfer Protocol) request header.
For sending multipart/formdata, you need to avoid contentType, since the browser automatically assigns the boundary and Content-Type.
In your case by using fetch, even if you avoid Content-Type it sets to default text/plain. So try with jQuery ajax. which removes the contentType if we set it to false.
This is the working code
var data = new FormData();
var imagedata = document.querySelector('input[type="file"]').files[0];
data.append("data", imagedata);
$.ajax({
method: "POST",
url: fullUrl,
data: data,
dataType: 'json',
cache: false,
processData: false,
contentType: false
}).done((data) => {
//resolve(data);
}).fail((err) => {
//console.log("errorrr for file upload", err);
//reject(err);
});