小程序实现真正多文件上传(multipart/form-data)

5,449 阅读2分钟

简介

小程序官方提供的api wx.uploadFile一次只能上传一个文件,一般的解决方案是调用多次,但是存在最大并发限制10,wx-multipart实现了一般的content-type 为 multipart/form-data的post请求。

原理

对于post请求,小程序官方提供了wx.requestwx.uploadFile。但是wx.uploadFile一次只能上传一个文件,那可不可以利用wx.request呢?Yes
根据官方文档data可以为ArrayBuffer类型,那么只需要解决两个问题。

1. 设置content-type为multipart/form-data;

wx.request({
	url,
	data: buffer,
	header: {
		'content-type': `multipart/form-data; boundary=${Boundary}`
	},
	method: 'post'
})

2. 将字符串和文件转化为ArrayBuffer

首先看下multipart/form-data的数据格式 rfc7578

------WebKitFormBoundarycf2702ca043fdf6c4fd42
Content-Disposition: form-data; name="number"

13812345678
------WebKitFormBoundarycf2702ca043fdf6c4fd42
Content-Disposition: form-data; name="image"; filename="1.png"
Content-Type: image/png

imageFileData
------WebKitFormBoundarycf2702ca043fdf6c4fd42--

数据body被Boundary分隔成几部分,Boundary的值为content-type里的boundary。 每部分包含Content-Disposition,name,filename,换行符\r\n,Content-Type和内容
最后以'--'+Boundary+'--'结尾
了解了body格式后,很容易根据field的name和value生成相应字符串

字符串转化为ArrayBuffer

由于js字符串编码格式是utf-16,需要先转化为utf-8,再转化为ArrayBuffer

let strToBuf = function(utf16Str) {
	let utf8Arr = []
	let byteSize = 0
	for (let i = 0; i < utf16Str.length; i++) {
		let code = utf16Str.charCodeAt(i)

		if (code >= 0x00 && code <= 0x7f) {
			byteSize += 1
			utf8Arr.push(code)
		} else if (code >= 0x80 && code <= 0x7ff) {
			byteSize += 2
			utf8Arr.push(192 | (31 & (code >> 6)))
			utf8Arr.push(128 | (63 & code))
		} else if (
			(code >= 0x800 && code <= 0xd7ff) ||
            (code >= 0xe000 && code <= 0xffff)
		) {
			byteSize += 3
			utf8Arr.push(224 | (15 & (code >> 12)))
			utf8Arr.push(128 | (63 & (code >> 6)))
			utf8Arr.push(128 | (63 & code))
		} else if (code >= 0x10000 && code <= 0x10ffff) {
			byteSize += 4
			utf8Arr.push(240 | (7 & (code >> 18)))
			utf8Arr.push(128 | (63 & (code >> 12)))
			utf8Arr.push(128 | (63 & (code >> 6)))
			utf8Arr.push(128 | (63 & code))
		}
	}
	let arrayBuf = new ArrayBuffer(utf8Arr.length)
	let buf = new Uint8Array(arrayBuf)
	for (let i = 0; i < utf8Arr.length; i++) {
		buf[i] = utf8Arr[i]
	}
	return buf
}

读取文件内容

对于文件内容,小程序官方提供了FileSystemManager.readFile()方法

FileSystemManager.readFile({
    filePath,//要读取的文件的路径 如果不传 encoding,则以 ArrayBuffer 格式读取文件的二进制内容
    success(res){
        
    }
})

文件content-type

对于文件的content-type 利用库mime-types根据文件名获取

总结

wx-multipart 欢迎star