我一直在尝试使用Axios将文件(图像或东西)上传到Spring启动REST服务器,但它根本无法正常工作。除文件外,其他有效负载均为find。例如,我发送字符串,整数和图像,但服务器只接收字符串和整数。奇怪的是,当我尝试使用Postman进行操作时,一切都很顺利,包括文件。
@PostMapping
@PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_MASTER')")
public ResponseEntity<ApiResponse> uploadProduct(@ModelAttribute ProductRequest productRequest) {
System.out.println("-----------------");
System.out.println("name: " + productRequest.getName());
System.out.println("price: " + productRequest.getPrice());
System.out.println("is empty: " + productRequest.getImage().isEmpty());
productRequest.getImage().forEach(o-> System.out.println(o.getOriginalFilename()));
System.out.println("-----------------");
return null;
}
这是我的测试控制器。 .getImage()始终返回true。
onSubmit() {
const data = new FormData();
data.append('name', this.name);
data.append('price', this.price);
data.append('files', this.image);
const token = localStorage.getItem('token');
const auth = {
headers: { Authorization: 'Bearer '.concat(token) },
};
axios.post('http://192.168.0.2:8080/api/products/', data, auth).then((response) => {
if (response.status === 200) {
alert('okay');
} else if (response.status === 401) {
alert('not okay');
}
}).catch((error) => {
console.log(error);
});
}
这是上面的javascript代码。
另外,我在服务器上设置过滤器,允许CORS如下所示。
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
if (!(request.getMethod().equalsIgnoreCase("OPTIONS"))) {
try {
chain.doFilter(req, res);
} catch(Exception e) {
e.printStackTrace();
}
} else {
System.out.println("Pre-flight");
response.setHeader("Access-Control-Allow-Methods", "POST,GET,DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "authorization, content-type," +
"access-control-request-headers,access-control-request-method,accept,origin,authorization,x-requested-with");
response.setStatus(HttpServletResponse.SC_OK);
}
}
Vue.js dev服务器端口:8081,Spring:8080,我附上了请求标头和有效负载的屏幕截图。
感谢您的阅读。
General
Request URL: http://192.168.0.2:8080/api/products/
Request Method: POST
Status Code: 200
Remote Address: 192.168.0.2:8080
Referrer Policy: no-referrer-when-downgrade
Response Headers
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS, DELETE
Access-Control-Allow-Origin: *
Access-Control-Max-Age: 3600
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: application/json;charset=UTF-8
Date: Tue, 29 May 2018 15:12:43 GMT
Expires: 0
Pragma: no-cache
Transfer-Encoding: chunked
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Request Headers
Provisional headers are shown
Accept: application/json, text/plain, */*
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxMiIsImlhdCI6MTUyNzYwNTg1MywiZXhwIjoxNTI3NjkyMjUzfQ.GJSdT0YgpKVhai9MQN6tPRDNgmmnCPchwNiLY_XUGlwQE-aMZcUpANEIvaz6bRlLSkHYnceJ63rx1fJIdxyE_Q
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryW5gjajdcI3pBL6q7
Origin: http://localhost:8081
Referer: http://localhost:8081/menu
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36
Request Payload
------WebKitFormBoundaryW5gjajdcI3pBL6q7
Content-Disposition: form-data; name="name"
114d
------WebKitFormBoundaryW5gjajdcI3pBL6q7
Content-Disposition: form-data; name="price"
1234
------WebKitFormBoundaryW5gjajdcI3pBL6q7
Content-Disposition: form-data; name="files"
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAooAAAKKCAYAAAC6dHqMAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAHH8SURBVHja7L15tKRVfe/99KlTdRhalB6kmRsaep5o6Hmkm2boBppmbKbuRoMxQUQB5XVIJBI1MSES0cQgMUqMYoxGvSGJYnIjmsHEKMQbTda7zIu5vvdN9I+bEF33z/Pub9Xep361z1NzPTU89fms9V0i9Dmn+pw6+/nu35gkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI051eksp/Pa1Fn+YwEAAAAgh+bv/B7pvB4J8wkAAACQoQlsx+At6ZHOz0CYSgAAAIA2jGCvzd8FPdKSDNSNqcREAgAAQO4MYS9MYDsG78KgyWLpzkhHjWr+28RE4RL7sT00nJ2az1ZNJAYSAAAARtYQ9sIEzjZ/pam3OD0uFadO+LrRdIb6+mxNfTa8Dq+3WCPahfnsxERiHgEAAGCgprBdQ9iJCXy3N4DPekP2g4wNYL8UDOazM8bSm8o2I5rtGEjMIwAAAPTNGLZrCGfMj8yQjQYaI5iJMTtj9frp87bubKr5i5cMk5n8Tvl7Upr6qDeSdxYmi4daNJGtmEeMIwAAAPTEFJ7fiSGUsXEG554szKBMXTB4O+95oKwDjzw6feTJp8t6zTPPTT/4ze/1XPq84WsEHX7siZnXEJSxAVVk9eshxd2igWzFOGIaAQAAYJY57MQUGkM4ubecOq0awu90Y4Tmzl9YNljL9x8sm659Dz08Y8qyMH/9lDWa+nvp77fu+iPlv68in91HIcu1ku9usS6ynnEk2ggAADDGxvC8To2hN4X3mOaRl7pJB1sjeOxTz4y8EeyF7n3u+ZpIZTCSMtAd1UMqfe1+ZlH9Y6umkWgjAADAGJjDRlHDetHCpVGk8KVOIoObjt5dNjxZpoTHzUQqza7vawcG8gflqGPnxhHDCAAAkENz2NQY+iaTexSBareeUBFCRb6CIZShwdj1P6Wt779S9m2Yx+/4iONRXQwwjQAAAJjDC0zEMBjD77RjChXNCs0jGLXhNY9KX+tn1WL940u+xvEtDaKN9WoaMY0AAAAjbA7LxtB1yF7njMB7Wo0YqmNXEapQR4gBG+20dZvG8VlvGje2YRoxjAAAAKNkDk3U8AetGEOlkBUtpJ4w/8ZRP2f9vFtIVT/r3kevS9LT02mpaQwjAABAnw1ie+bQpRGbNaBgDFGQus4VbWwy97GcnlZkuolpxDACAACMojlUp6xSyYyjQV2axu82SE1jGAEAAPpsEFNrDpuZQ6UVFTVUfRrdyKhT09ggPf2SShvcjM19KVFGDCMAAEAGBjEteujNoRt6XWlI+UGjlLIe7jSgoF5Klw01N9WPMpZnNB7FMAIAAGRrENNSy6/zg68bRg5JKaN+jN7RzMYGqemvYxgBAACyM4hlc6j6r2bRw5BWxsCgQUiNUKp77dIwLuJIAAAAaNEgqt6rUe2h5uDpAU3NIRoWqcyhS8N4NtFFAADAIDYwiHqYeoNYN3pIahkNe1pa79M67+EvN2h66Ud00c4ibVekyQEAYJAGMX1biurANM6G6CHKjWEsTX3MlVVsigxjFtHF2Bie3wNhHAEAoCcs6sYgKo2n9DKmA+U0Jf2Sq799q/s9WNbj6GK9DUbxNIFOlWYcMY0AANDWg+rspGkNYn2DyFgblEfDWKdL+usp6eh2o4vtrrfsVvWMI4YRAADaiiLWa1LBIKKxlMoo0oZ3u+jiB9zvyHJvGFuNLrazwSj8Hl7oBtUf0iYj9zUfl/zv5NeNng3/rfzniqU7k9nbZ9h1DQAAXUcRw5ibTXoQpnUxYxDRuEn1tnXqF7/rTNzhpDYdbU3Y6Sm/b62stzyq7TH1ovht6Dv6PDKPCbuuAQBGwpw162Dsx0HdKIq4zA/K/gEGEaHW0tHuUvVLdaKLS+oYxI53n3ehl9hEAwAwvIawm27FXhnIhlHEenWIejDSpIJQNbqodZMpRuxvUzqjL0garLf0A+ofbzSgPmwx0kVNX1cbZpQOl2kN0u+n/r2kP9dgA03QD9zXfUvCYHEAgIGYwzTT14tuxW46FutFEfWgWJ6WZtbDSQ8ezAFCLUcXX3IRu+NJJRWdluptWvtr11vKAGpsT6fjfvTx+jxpNZaRYVzWwDCezvEOANBbc5hm+rrtVEwzj62axtPrRBGXqb5KdVbxA2T5/oMdPShow more
并且控制台说:
log.js?4244:23 [HMR] Waiting for update signal from WDS...
Menu.vue?64be:224 eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxMiIsImlhdCI6MTUyNzYwNTg1MywiZXhwIjoxNTI3NjkyMjUzfQ.GJSdT0YgpKVhai9MQN6tPRDNgmmnCPchwNiLY_XUGlwQE-aMZcUpANEIvaz6bRlLSkHYnceJ63rx1fJIdxyE_Q
Menu.vue?64be:228 {headers: {…}}
Menu.vue?64be:233 [__ob__: Observer]
Menu.vue?64be:230 {data: Array(22), status: 200, statusText: "", headers: {…}, config: {…}, …}
vue.esm.js?efeb:591 [Vue warn]: Invalid prop: type check failed for prop "src". Expected String, got File.
warn @ vue.esm.js?efeb:591
assertProp @ vue.esm.js?efeb:1632
validateProp @ vue.esm.js?efeb:1560
createFunctionalComponent @ vue.esm.js?efeb:4045
createComponent @ vue.esm.js?efeb:4250
_createElement @ vue.esm.js?efeb:4420
createElement @ vue.esm.js?efeb:4357
vm._c @ vue.esm.js?efeb:4489
render @ Menu.vue?0af2:380
Vue._render @ vue.esm.js?efeb:4544
updateComponent @ vue.esm.js?efeb:2788
get @ vue.esm.js?efeb:3142
run @ vue.esm.js?efeb:3219
flushSchedulerQueue @ vue.esm.js?efeb:2981
(anonymous) @ vue.esm.js?efeb:1837
flushCallbacks @ vue.esm.js?efeb:1758
vue.esm.js?efeb:591 [Vue warn]: Invalid prop: type check failed for prop "src". Expected String, got File.
warn @ vue.esm.js?efeb:591
assertProp @ vue.esm.js?efeb:1632
validateProp @ vue.esm.js?efeb:1560
createFunctionalComponent @ vue.esm.js?efeb:4045
createComponent @ vue.esm.js?efeb:4250
_createElement @ vue.esm.js?efeb:4420
createElement @ vue.esm.js?efeb:4357
vm._c @ vue.esm.js?efeb:4489
render @ Menu.vue?0af2:659
Vue._render @ vue.esm.js?efeb:4544
updateComponent @ vue.esm.js?efeb:2788
get @ vue.esm.js?efeb:3142
run @ vue.esm.js?efeb:3219
flushSchedulerQueue @ vue.esm.js?efeb:2981
(anonymous) @ vue.esm.js?efeb:1837
flushCallbacks @ vue.esm.js?efeb:1758
vue.esm.js?efeb:591 [Vue warn]: Invalid prop: type check failed for prop "src". Expected String, got File.
warn @ vue.esm.js?efeb:591
assertProp @ vue.esm.js?efeb:1632
validateProp @ vue.esm.js?efeb:1560
createFunctionalComponent @ vue.esm.js?efeb:4045
createComponent @ vue.esm.js?efeb:4250
_createElement @ vue.esm.js?efeb:4420
createElement @ vue.esm.js?efeb:4357
vm._c @ vue.esm.js?efeb:4489
render @ Menu.vue?0af2:839
Vue._render @ vue.esm.js?efeb:4544
updateComponent @ vue.esm.js?efeb:2788
get @ vue.esm.js?efeb:3142
run @ vue.esm.js?efeb:3219
flushSchedulerQueue @ vue.esm.js?efeb:2981
(anonymous) @ vue.esm.js?efeb:1837
flushCallbacks @ vue.esm.js?efeb:1758
Menu.vue?64be:348 114d
Menu.vue?64be:354 eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxMiIsImlhdCI6MTUyNzYwNTg1MywiZXhwIjoxNTI3NjkyMjUzfQ.GJSdT0YgpKVhai9MQN6tPRDNgmmnCPchwNiLY_XUGlwQE-aMZcUpANEIvaz6bRlLSkHYnceJ63rx1fJIdxyE_Q
Menu.vue?64be:359 {headers: {…}}headers: Authorization: "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxMiIsImlhdCI6MTUyNzYwNTg1MywiZXhwIjoxNTI3NjkyMjUzfQ.GJSdT0YgpKVhai9MQN6tPRDNgmmnCPchwNiLY_XUGlwQE-aMZcUpANEIvaz6bRlLSkHYnceJ63rx1fJIdxyE_Q"__proto__: constructor: ƒ Object()hasOwnProperty: ƒ hasOwnProperty()isPrototypeOf: ƒ isPrototypeOf()propertyIsEnumerable: ƒ propertyIsEnumerable()toLocaleString: ƒ toLocaleString()toString: ƒ toString()valueOf: ƒ valueOf()__defineGetter__: ƒ __defineGetter__()__defineSetter__: ƒ __defineSetter__()__lookupGetter__: ƒ __lookupGetter__()__lookupSetter__: ƒ __lookupSetter__()get __proto__: ƒ __proto__()set __proto__: ƒ __proto__()__proto__: Object