后面效果需要查看调试器的 Network。
起因
今天下午下班前,后端的小伙伴突然问了我一个问题,“前端 POST 的 FormData 能不能设置 Content-Type”。
嘿,当然能啊,不就是请求头的 Content-Type 吗?
乌龙后才发现,他问我的是,能不能给 FormData 的单个字段设置 Content-Type,我当时就懵逼了,额,好像,不行吧。
调研过程
这激起了我的斗志,因为他说,PostMan 可以设置单个字段的 Content-Type,我盯着看了一下,确实可以,想看下控制台,结果这兄弟用的是客户端,懒得抓包,就回去自己折腾去了。
我们知道,new FormData 的实例,对于字段设置有两个方法,append 与 set,二者的区别是:
append是追加字段,而 set 是添加或替换,即 set 的时候,该字段如果已经存在,则更新当前字段。
let form_data = new FormData()
('a', 1)
('a', 2)
('b', 3)
('b', 4)
fetch('/test', {
method: 'POST',
body: form_data,
})
抓包看效果即可
我确定就这个两个 api 可以给 FormData 添加字段,很自觉地去复习 MDN 文档。
文档说得很清楚,append 与 set 在传参方面一样,都是三个,文件的 name,就是常说的 key,value 就是值或者文件/图片啥的,最后一个是显式的告诉后端我们的文件名,常见伪代码如下:
('file', file[, 'filename'])
很明显嘛,没有对这个 name 的值 value 设置 Content-Type 的地方。
就在我准备明确告知不行的时候,多年的踩坑经验告诉我,事情没那么简单……
抱着稳妥方案,Google 了一下,果然找到了相关的问题:
当我看到这个答复的时候,一巴掌抽到自己嘴丫上,怎么就没想到呢,使用 BloB 构建一个类文件对象不就完了!挺萌的,File 不就是继承自 Blob 的嘛。
HTML
<button onclick="clickSubmit()">submit</button>
JS
function clickSubmit() {
let form_data = new FormData()
('json_key', new Blob([JSON.stringify({a: 1, b: 2})], {type: 'application/json'}))
aler('json_key').type)
fetch('/test_post', {
method: 'post',
body: form_data
}).catch(error => {
con(error)
})
}
嘿,这不就好了?
有啥用
讲道理,我是没遇到过,一般来说,这种 FormData 上传文件带其他字段,都是直接 JSON.stringify 处理后的字符串上传的。
不过小伙伴说的场景是,如果支持具体的字段的话,就可以在 API 文档上体现出更可读的数据结构表达,想想,有道理。
总结
个人认为这个知识点不难,但是挺偏的……作为知识储备还是很好的,如果有兴趣,更可以深入地挖掘下 Blob,毕竟图片处理的时候,这个还是很常用的东西~