FormData 是什么?附多文件上传案例
作者:互联网
前言
前端上传数据时,必定会用 FormData 进行封装。FormData 是一种 key-value 的集合。发送数据时要考虑 FormData 中的数据都是什么格式的数据,即设置 content-type。
表单上传过程
这是一个表单的案例:
<form id="form" action="http://localhost:8080/upload/user/info" method="post">
<input id="uname" type="text" name="uname" />
<input id="pwd" type="password" name="pwd" />
<input type="submit" value="Submit!" />
</form>
用户只需要点击最后一个 input,就可以提交所有表单里面的数据到服务器。这里什么 JS 代码都没有写,到底是如何做到的呢?
查阅 MDN 文档,实际上前端发送数据使用的是 XMLHttpRequest。表单数据通过 XMLHttpRequest.send() 请求特定 URL。XMLHttpRequest 在 AJAX 编程中被大量使用。XMLHttpRequest.send() 函数可以发送的数据类型有以下几种:
XMLHttpRequest.send();
XMLHttpRequest.send(ArrayBuffer data);
XMLHttpRequest.send(ArrayBufferView data);
XMLHttpRequest.send(Blob data);
XMLHttpRequest.send(Document data);
XMLHttpRequest.send(DOMString? data);
XMLHttpRequest.send(FormData data);
表单使用这个函数发送数据的,那又是哪一个参数类型的函数呢?其实就是最后一个函数。继续往下看,FormData 是什么?
FormData
FormData 是用来表示表单数据的键值对集合。表单其实就是一个 Map 集合,但是,直接用 Map 作为 XMLHttpRequest.send() 的发送数据是不合适的。因为,发送之前要做很多封装工作。所以,就出现一个新的概念——FormData。
FormData 可以被当做是一个 Map 来对待,它也有和 Map 类似的函数,例如:get、entries。
作用
有了 FormData,我们可以自定义发送表单数据,可以添加表单没有设置的一项数据。使用 AXIOS 或 AJAX,可以轻松地、自由地把表单数据发送出去。
set()
FormData 提供一个 set(name, value) 函数。它可以添加数据到 FormData 中。而 name 相当于一个表单中属性名为 name 的 input,value 就是用户在 input 中输入的数据。
<input id="uname" type="text" name="uname" />
这个 input 标签用 FormData 表示就是:
let formData = new FormData();
formData.set('hobbies', ['football', 'animation', 'cycling']);
formData.get('hobbies');
append()
append(name, value) 也可以添加数据到 FormData 中。
formData.append('hobbies', ['football', 'animation', 'cycling']);
formData.get('hobbies');
append() 和 set()
append 和 set 都可以添加数据到 FormData 中。但是实际使用中有着很大的区别,下面将通过实验来证明。
(一)set
formData.set('hobbies', 'football');
console.log(`第一次 value:${formData.getAll('hobbies')}`);
formData.set('hobbies', 'animation');
console.log(`第二次 value:${formData.getAll('hobbies')}`);
(二)append
formData.append('hobbies', 'football');
console.log(`第一次 value:${formData.getAll('hobbies')}`);
formData.append('hobbies', 'animation');
console.log(`第二次 value:${formData.getAll('hobbies')}`);
通过实验证明,如果第二次添加的 key 与第一次的 key 是相同的,set 会覆盖第一次 value 中的数据;append 会追加到 value 之后。
案例:多文件上传
前端
多文件上传的前端使用 Vue3 框架。首先,引入 Vue3 和 Axios。
<script src="https://unpkg.com/vue@next"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
因为 FormData 就是用来描述一个表单的,所以,在 html 中甚至都不需要 form 标签。
<div id="app">
<input @change="selected($event)" type="file" multiple />
<button @click="submit">提交</button>
</div>
下面是完整的 Vue3 代码:
Vue.createApp({
data() {
return {
files: []
};
},
methods: {
selected(e) {
this.files = e.target.files;
},
submit() {
let formData = new FormData();
for (let index in this.files) {
formData.append(`files`, this.files[index]);
}
axios.post('http://localhost:8080/upload/files', formData, {
headers: {
'content-type': 'multipart/form-data'
}
});
}
}
}).mount('#app');
(1)当用户选择了文件之后,input 会触发 change 事件,Vue3 使用$event
接收事件的数据。(2)当用户点击提交按钮之后,首先构造一个 FormData 对象。然后把所有的文件对象存放到 FormData 中,因为是多文件,可以用 append 添加一个 key 为 files 的;value 为数组类型的键值对数据。(3)通过 axios.post() 提交数据给后端处理,并且指定 content-type 是 multipart/form-data 格式。
后端
@RequestMapping(value = "/upload/files", consumes = "multipart/form-data")
public void uploadFiles(@RequestBody MultipartFile[] files) {
System.out.println(files[0].getOriginalFilename());
System.out.println(files[1].getOriginalFilename());
}
前端发送过来的数据是一个数组,一个元素对应一个 MultipartFile。files 一定要和前端 FormData.append() 当中的 name 对应。
打印结果:
content-type
application/x-www-form-urlencoded
表单的默认的 content-type 是 application/x-www-form-urlencoded;charset=UTF-8。对于这样的 content-type,接口只能通过 @RequestParam 来接收这些数据。
@RequestMapping(value = "/upload/user/info", method = RequestMethod.POST, consumes = "application/x-www-form-urlencoded;charset=UTF-8")
public void uploadUserInfo(@RequestParam(name = "uname") String uname, @RequestParam(name = "pwd") String pwd) {
System.out.println("uname: " + uname + ", pwd: " + pwd);
}
multipart/form-data
标签:files,XMLHttpRequest,FormData,value,表单,附多,上传,formData 来源: https://www.cnblogs.com/shiramashiro/p/16205162.html