fetch自定义批量发送new formdata()数据(对象加文件)
最近写图片批量上传同时携带对象数据功能,遇到了一些问题,前端使用element-plus的el-upload组件,但是因为是自定义fetch传送,所以传输还是等同于原生, 后端使用express的multer中间件接收. 首先表单数据一般须声明multipart/form-data以及input的type
采用Formdata传送数据,数据会被转换为特殊的包含键值的字符串,其中append的第一个参数只是告知后端,后端解析数据时的键,固定语法,
const fd = new FormData()
fd.append("file", rawfile)
fd.append("text", "你好")
fetch(url, {method: "post", body: fd}) // 后面省略 用fetch传递时不要指定content-type, 因为涉及boundary分隔,默认就好
当我们传普通数据时,直接append就行,每次append类似于在传输数据数组里push了一个值,批量的普通数据,直接放数组里for循环append就行,但是都要指定append第一个形参,形参相同的话,批量数据就会放在一个数组里;
而复杂数据就要讨论格式转换问题了,之前以为json转换及解析下就行了,然后就会收到TypeError: Cannot convert object to primitive value 的报错. 因为复杂数据后端打印貌似是json格式,但实际嵌套的value都变成了字符串
// 此数据参考 详细内容可以查看 https://www.cnblogs.com/wonyun/p/7966967.html
var obj =
{ a: '2',
b: {c: 'test'},
c: [ {id: 1, name: 'xx'}]
}
//假如item是append的第一个参数,最终转换的formData数据是这样的
item[a]: 2
item[b][c]: test
item[c][][id]: 1
item[c][][name]: xx
// 生成的各种类型数据都会以这样独立的字符串 类似数组的形式 传输
所以对象要么只能以键值对的形式单独append,且value还必须是普通数据
要么要自己构造一个转换函数,此处参考这位大佬的文章
function objectToFormData (obj, form, namespace) {
const fd = form || new FormData();
let formKey;
for(var property in obj) {
if(obj.hasOwnProperty(property)) {
let key = Array.isArray(obj) ? '[]' : [${property}]
;
if(namespace) {
formKey = namespace + key;
} else {
formKey = property;
}
// if the property is an object, but not a File, use recursivity.
if(typeof obj[property] === 'object' && !(obj[property] instanceof File)) {
objectToFormData(obj[property], fd, formKey);
} else {
// if it's a string or a File object
fd.append(formKey, obj[property]);
}
}
}
return fd;
}
//膜拜大神的递归,不过此函数有个小问题,当对象为空时数据不会被append执行
{"_id":34564645,"name":"436546","num":"546546", urls: []}
//最终只会得到
obj[_id] 34564645
obj[name] 436546
obj[num] 546546
//并没有期待的 // 也许可以改进,目前不确定是函数问题还是formdata本身解析问题
obj[urls][]
//formData查看方法 let fd = new formData()
for (var [a, b] of fd.entries()) {
console.log(a, b);
}
下面是大致实现的代码
let res = await api.batchUploadImg(fileArr , toRaw(formData.self)) //批量上传
// function objectToFormData 此处省略上面大神写的封装函数...
// 数据上传函数
async batchUploadImg(files, item) {
try { //此处为了后端获取数据分开 files 是原始图片文件的数组, item为对象数据
const body1 = new FormData()
for (let i = 0; i < files.length; i++) {
body1.append("images", files[i]);
}
let body = objectToFormData(item, body1, "data")
let rawres = await fetch('http://url',
{ method: 'post', body, headers:
{ 'Authorization': window.localStorage.getItem('accessToken')}})
return rawres.json()
} catch (error) {
return {status: 999, message:'网络错误或者服务器未开启'}
}
}
// 后端multer配置
const upload = multer({ storage }) //multer会在storage此处直接存储不带后缀名的文件,所以需要中间函数进行处理
const uploadRouter = require('./router/upload')
app.use('/upload', upload.any(), uploadRouter)
// 后端获取数据
let item = req.body.data // multer会将数据放到req.body里, 文件放入req.files
let files = req.files