React.js,如何发送 multipart/form-data 到服务器
React.js, how to send a multipart/form-data to server
我们想发送一个图片文件作为multipart/form到后端,我们尝试使用html表单获取文件并将文件作为formData发送,代码如下
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>
)
}
}
后台错误是"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".
阅读this后,我们尝试在fetch中将边界设置为headers:
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!");
});
}
这一次,后端的错误是:Servlet.service() for servlet [dispatcherServlet] in context with path [] throw exception [Request processing failed;嵌套异常是 java.lang.NullPointerException],根本原因是
我们添加多部分边界对吗?它应该在哪里?
也许一开始我们错了,因为我们没有得到 multipart/form 数据。怎样才能正确获取呢?
为了发送multipart/formdata
,你需要避免contentType
,因为浏览器会自动分配boundary
和Content-Type
。
在您的情况下,使用 fetch
,即使您避免使用 Content-Type
,它也会设置为默认值 text/plain
。所以尝试 jQuery ajax
。如果我们将它设置为 false,它会删除 contentType
。
这是工作代码
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);
});
我们只是尝试删除我们的 headers 并且成功了!
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!");
});
该文件也可在活动中使用:
e.target.files[0]
(无需 document.querySelector('input[type="file"]').files[0];
)
uploadAction(e) {
const data = new FormData();
const imagedata = e.target.files[0];
data.append('inputname', imagedata);
...
注:
使用console.log(data.get('inputname'))
进行调试,console.log(data)
不会显示附加数据。
React 文件上传组件
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;
这是我通过 axios.
预览图片上传的解决方案
import React, { Component } from 'react';
import axios from "axios";
反应组件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;
https://muffinman.io/uploading-files-using-fetch-multipart-form-data/ 最适合我。它使用 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>
);
}
}
the request was rejected because no multipart boundary was found".
当您发送 multipart/form-data
时,边界会自动添加到请求 header 的 content-type 中。当参数以边界规则结束时,您必须告诉服务器。你必须像这样设置 Content-type
"Content-Type": `multipart/form-data: boundary=add-random-characters`
这篇文章为您提供指导: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.
我们想发送一个图片文件作为multipart/form到后端,我们尝试使用html表单获取文件并将文件作为formData发送,代码如下
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>
)
}
}
后台错误是"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".
阅读this后,我们尝试在fetch中将边界设置为headers:
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!");
});
}
这一次,后端的错误是:Servlet.service() for servlet [dispatcherServlet] in context with path [] throw exception [Request processing failed;嵌套异常是 java.lang.NullPointerException],根本原因是
我们添加多部分边界对吗?它应该在哪里? 也许一开始我们错了,因为我们没有得到 multipart/form 数据。怎样才能正确获取呢?
为了发送multipart/formdata
,你需要避免contentType
,因为浏览器会自动分配boundary
和Content-Type
。
在您的情况下,使用 fetch
,即使您避免使用 Content-Type
,它也会设置为默认值 text/plain
。所以尝试 jQuery ajax
。如果我们将它设置为 false,它会删除 contentType
。
这是工作代码
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);
});
我们只是尝试删除我们的 headers 并且成功了!
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!");
});
该文件也可在活动中使用:
e.target.files[0]
(无需 document.querySelector('input[type="file"]').files[0];
)
uploadAction(e) {
const data = new FormData();
const imagedata = e.target.files[0];
data.append('inputname', imagedata);
...
注:
使用console.log(data.get('inputname'))
进行调试,console.log(data)
不会显示附加数据。
React 文件上传组件
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;
这是我通过 axios.
预览图片上传的解决方案import React, { Component } from 'react';
import axios from "axios";
反应组件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;
https://muffinman.io/uploading-files-using-fetch-multipart-form-data/ 最适合我。它使用 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>
);
}
}
the request was rejected because no multipart boundary was found".
当您发送 multipart/form-data
时,边界会自动添加到请求 header 的 content-type 中。当参数以边界规则结束时,您必须告诉服务器。你必须像这样设置 Content-type
"Content-Type": `multipart/form-data: boundary=add-random-characters`
这篇文章为您提供指导: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.