I am working on file upload React with web API. After uploading the file, server side shows that
null.,..................................................................
import React, { PropTypes } from 'react';
import axios from 'axios';
class Dashboard extends React.Component {
constructor(props){
var files;
super(props);
this.state = {
selectedFile: null
}
}
fileChangedHandler = event => {
this.setState({
selectedFile: event.target.files[0]
})
var file = this.refs.file.files[0].name;
let reader = new FileReader();
reader.onloadend = () => {
this.setState({
imagePreviewUrl: reader.result
});
}
reader.readAsDataURL(event.target.files[0])
}
async submit(e){
e.preventDefault();
await this.addImage(this.state.selectedFile);
};
addImage = async (file) => {
console.log(this.state.selectedFile);
await fetch('http://localhost:32188/Api/Authenticate/Uploadfile',
{
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: this.state.selectedFile
}
)
}
render() {
<form onSubmit={e => this.submit(e)} enctype="multipart/form-data">
<input ref="file" type="file" name="user[image]" onChange={this.fileChangedHandler} style={{padding: '5px', marginLeft: '31px'}} />
<div className="signin_form_button">
<input type="submit" value="Upload" className="signin_form_buttonstyle" />
</div>
</form>
}
}
Serverside Code
Model
public class ImageModel
{
public IFormFile File { get; set; }
}
Controller
[System.Web.Http.Route("Api/Authenticate/Uploadfile")]
[System.Web.Http.HttpPost]
public void CreateImage([System.Web.Http.FromBody] ImageModel model)
{
var file = model.File;
}
Following Error Message is displayed
500 Internal Server Error Occurred
Message: "An error has occurred."
ExceptionMessage: "Object reference not set to an instance of an object."
Please Help.
Link:https://codesandbox.io/s/vigorous-mestorf-osf90
Related
I have two components, Client and App, and a fetch function. App is the child component of Client. I want to update Client's state using the return value from the method App calls. However, Client's state response is undefined after the fetch. I'm not sure why this code does not work.
import React, { Component } from 'react';
import './App.css';
function post(user, token, data){
console.log('fetching...')
fetch(`/project`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic '+ btoa(user+':'+token),
},
body: JSON.stringify(data)
}).then(r => {
if (!r.ok)
throw Error(r.status);
r.json().then(r => {return(r)});
}).catch(error => {throw Error(error)})
}
class Client extends Component {
constructor(props) {
super(props);
this.state = {
user: '',
token: '111',
project: {'project':'demo'},
response: {},
};
this.updateState = this.updateState.bind(this);
};
updateState(){
const { user, token, project } = this.state;
post(user, token, project).then(text => this.setState({ response: text
}));
}
render() {
return (
<App updateState={this.updateState}/>
)
}
}
class App extends Component {
render() {
return (
<div className="App">
<button onClick={ () => {
this.props.updateState()} }>Fetch Project</button>
</div>
);
}
}
EDIT: I changed my post() to this and it works :)
async function post(user, token, data){
console.log('fetching...')
const response = await fetch(`/project`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic '+ btoa(user+':'+token),
},
body: JSON.stringify(data)
}).catch(error => {throw Error(error)});
if(!response.ok)
throw Error(response.status);
const obj = await response.json();
return(obj);
}
If you are working with promises, you can do something like this.
import React, { Component } from "react";
async function post() {
// fetch //
return await fetch("https://hipsum.co/api/?type=hipster-centric");
}
class Client extends Component {
constructor(props) {
super(props);
this.state = {
response: "12"
};
this.updateState = this.updateState.bind(this);
}
async updateState(res) {
const text = await res().then(res => res.text());
this.setState({ response: text });
}
render() {
return (
<>
{this.state.response}
<App updateState={this.updateState} />
</>
);
}
}
class App extends Component {
render() {
return (
<div>
<button
onClick={() => {
this.props.updateState(post);
}}
>
Fetch
</button>
</div>
);
}
}
export default Client;
sandbox
It will be nice to know all the code for the fetch function but I think the problem is mostly here:
this.props.updateState(post())
That call is synchronous and the fetching process isn't. You need a better approach with await or promises or a callback.
I create some spring + react.js application and one of the main function of that is show news from the MySQL database. But when I try to fetch JSON using react function that returns me nothing.
I added 'Content-Type': 'application/json', 'Accept': 'application/json' to the fetch React function, but that not help.
React component:
class News extends Component {
constructor(props) {
super(props);
this.state = {
news: []
}
};
ourFunction = () =>{
const response = fetch('/news/all',{
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
console.log(response);
const body = response.json;
console.log(body);
this.setState({news : body });
console.log(this.state.news);
};
render() {
return (
<div className="news">
<span onClick={this.ourFunction}>Click{}</span>
<div className="news_bar"> Новини</div>
{defaultNewsData.map(({title, content, date}) => <AccordionContainer
title={title}
content={content}
date={date}
/>)}
</div>
);
}
}
export default News;
Spring controller:
#RestController
#RequestMapping(path = "/news")
public class NewsController {
#Autowired
private NewsRepository newsRepository;
#GetMapping("/add")
public String addNewNews(#RequestParam String title, #RequestParam String content){
News news = new News();
news.setTitle(title);
news.setContent(content);
newsRepository.save(news);
return "News Saved";
}
#GetMapping(path = "/all")
public Iterable<News> getAllNews(){
return newsRepository.findAll();
}
}
Expected: to fetch JSON object to the javascript and parse it to the HTML.
Actual: I receive nothing, that is shown on the console.
I had to create Axios POST where the body type is form-data. Some of keys are strings, and some are files. Postman request:
How to add upload buttons to fetch files into state, and how to make Axios request?
Simply trigger a method in onChange event on input of type "file" and send to server with "multipart/form-data" format:
<Input id="file" type="file" onChange={this.uploadFile} />
let formData = new FormData();
/*
Iteate over any file sent over appending the files
to the form data.
*/
for( var i = 0; i < this.files.length; i++ ){
let file = this.files[i];
formData.append('files[' + i + ']', file);
}
/*
Make the request to the POST /select-files URL
*/
axios.post( '/select-files',
formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(function(){
console.log('SUCCESS!!');
})
.catch(function(){
console.log('FAILURE!!');
});
import React, { Component } from 'react';
import axios from "axios";
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;
I'm trying to create a way to attach files from Reactjs Dropzone plugin to an axios POST request.
Currently, my module is doing the following ajax request:
submitPost() {
const formData = new FormData;
const err = this.validate();
if( !err ) {
this.setState({buttonText: 'Posting...'});
axios.post('/user/post/create/', {
content: this.state.post_content,
images: this.state.images,
headers: {
'Content-Type': 'multipart/form-data'
},
})
.then(response => {
console.log(response);
this.setState({buttonText: 'Success'});
setTimeout(
function() {
this.setState({
buttonText: 'Post',
post_content: '',
images: []
});
$('#post_content').val('');
}.bind(this), 1000
);
}).catch(error => {
console.log(error.response);
this.setState({buttonText: 'Error'});
setTimeout(
function() {
this.setState({buttonText: 'Post'});
}.bind(this), 1000
);
});
} else {
this.setState({buttonText: 'Error'});
setTimeout(
function() {
this.setState({buttonText: 'Post'});
}.bind(this), 1000
);
}
}
And the following states are defined:
constructor(props){
super(props);
this.state= {
progressValue: '0',
progressText: '0%',
buttonText: 'Post',
post_content: '',
images: []
}
}
And here is the Uploader Module i've written using Reactjs Dropzone:
import React, { Component } from 'react';
import Dropzone from 'react-dropzone'
import $ from "jquery";
export class Uploader extends Component {
constructor(props){
super(props);
this.state= {
images: this.props.images
}
}
onDrop(files) {
this.setState({
images: files
});
console.log(files);
this.props.handleImageUpload(files);
}
render() {
return (
<div className="uploader">
<div className="previews">
{this.state.images.map((file) =>
<div
className="preview"
key={file.preview}>
<img src={file.preview} />
</div>
)}
</div>
<Dropzone onDrop={this.onDrop.bind(this)}>
<p>Try dropping some files here, or click to select files to upload.</p>
</Dropzone>
</div>
);
}
}
Any help would be appreciated, I'm currently trying to upload the files from the Images state which comes through as an array, but comes through in this format:
[{"preview":"blob:http://outist.local/3c3fc96b-b89d-41c8-8835-3309be8ac430"},{"preview":"blob:http://outist.local/6cf9aa40-0538-4cef-affe-58951afef2eb"},{"preview":"blob:http://outist.local/4631977b-1301-498d-b4e8-611f9a57b6bb"},{"preview":"blob:http://outist.local/1650a49c-2eed-408c-a035-473cade2bfa6"}]
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);
});