uni-app 同时上传多个文件
最近在用uni-app写项目的时候 有业务需要同时上传多张图片给后台,但查了官方文档,发现uni.uploadFile()
虽然可以通过 files
参数上传多个文件,但是只支持APP和H5。
我的项目主要针对的是微信小程序,所以看了很多文章,找到如下两种方式,可以参考一下
方案一:使用 uni.uploadFile() 轮询上传
使用 uni.chooseMedia 成功后会返回 本地临时文件列表
可以参考官方文档 uni.chooseMedia()
uni.chooseMedia({
mediaType: ['image'],
sourceType: ['album'],
success: (res) => {
const { tempFiles } = res
// uploadFile 方法为自己封装的上传方法
uploadFile(tempFiles)
},
fail: () => {
console.log('fail')
},
complete: () => {
// console.log('complete')
},
})
成功后调用自己写的 uploadFile
const uploadFile = (files:any[]) => {
const uploadTasks = files.map((file: any, index: number) => {
return new Promise((resolve, reject) => {
const uploadTask = uni.uploadFile({
url: 'https://www.xxx.cn/v1/wxInvoice/upload', // 接口地址
filePath: file.tempFilePath, // 临时文件路径
name: 'files[]', // 服务器接收的文件字段名(这个地方很重要要和后端沟通一下)
header: {
Authorization: 'Bearer ' + token.value,
'Content-Type': 'multipart/form-data',
},
formData: {
// 可以在这里添加额外的formData参数
},
success: function (res) {
resolve(res.data)
},
fail: function (err) {
reject(err)
},
})
// 这里可以根据需要显示进度条
uploadTask.onProgressUpdate((res) => {
console.log('上传进度', res.progress)
console.log('已经上传的数据长度', res.totalBytesSent)
console.log('预期需要上传的数据总长度', res.totalBytesExpectedToSend)
})
})
})
Promise.all(uploadTasks)
.then((res) => {
console.log('上传成功', res)
// 上传成功后的操作
})
.catch((err) => {
console.log('上传失败', err)
// 上传失败后的操作
})
}
方案二
使用 wx-formdata 库,当然我这里是uni-app + ts
项目,所以我把这个库里的 formData.js
和 mimeMap.js
库进行了修改,文章结尾看(不要介意代码质量,能跑就行)
使用
uni.chooseMedia({
mediaType: ['image'],
sourceType: ['album'],
success: (res) => {
const { tempFiles } = res
const formData = new FormData()
// 非文件用 append()方法
// 文件用 appendFile()方法
// 需要遍历将文件加入到formData中(这里是我的需求,可自定义)
for (let i = 0; i < tempFiles .length; i++) {
formData.appendFile('files[' + i + ']', tempFiles [i].tempFilePath)
}
const data = formData.getData()
// 调用接口传递数据
uni.request({
url: '接口地址',
method: 'post',
data: data.buffer,
header: { 'Content-Type': data.contentType },
success:() => {
//...
}
})
},
fail: () => {
console.log('fail')
},
complete: () => {
// console.log('complete')
},
})
以下是修改过的文件
formData.ts
import mimeMap from './mimeMap'
interface CommonFormData {
fileManager: any
data: any
files: any
append(name: string, value: any): boolean
appendFile(name: string, path: any, fileName?: any): boolean
getData(): any
}
class FormData implements CommonFormData {
fileManager: any
data: any
files: any
constructor() {
this.fileManager = uni.getFileSystemManager()
this.data = {}
this.files = []
}
append(name: string, value: any) {
this.data[name] = value
return true
}
appendFile(name: string, path: any, fileName?: any) {
let buffer = this.fileManager.readFileSync(path)
if (Object.prototype.toString.call(buffer).indexOf('ArrayBuffer') < 0) {
return false
}
if (!fileName) {
fileName = getFileNameFromPath(path)
}
this.files.push({
name: name,
buffer: buffer,
fileName: fileName,
})
return true
}
getData() {
return convert(this.data, this.files)
}
}
function getFileNameFromPath(path: any) {
let idx = path.lastIndexOf('/')
return path.substr(idx + 1)
}
function convert(data: any, files: any) {
let boundaryKey = 'unimpFormBoundary' + randString() // 数据分割符,一般是随机的字符串
let boundary = '--' + boundaryKey
let endBoundary = boundary + '--'
let postArray: any = []
//拼接参数
if (data && Object.prototype.toString.call(data) == '[object Object]') {
for (let key in data) {
postArray = postArray.concat(formDataArray(boundary, key, data[key]))
}
}
//拼接文件
if (files && Object.prototype.toString.call(files) == '[object Array]') {
for (let i in files) {
let file = files[i]
postArray = postArray.concat(formDataArray(boundary, file.name, file.buffer, file.fileName))
}
}
//结尾
let endBoundaryArray = []
endBoundaryArray.push(...endBoundary.toUtf8Bytes())
postArray = postArray.concat(endBoundaryArray)
return {
contentType: 'multipart/form-data; boundary=' + boundaryKey,
buffer: new Uint8Array(postArray).buffer,
}
}
// 获取随机字符串
function randString() {
var result = ''
var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
for (var i = 17; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)]
return result
}
function formDataArray(boundary: any, name: string, value: any, fileName?: string) {
let dataString: any = ''
let isFile = !!fileName
dataString += boundary + '\r\n'
dataString += 'Content-Disposition: form-data; name="' + name + '"'
if (isFile) {
dataString += '; filename="' + fileName + '"' + '\r\n'
dataString += 'Content-Type: ' + getFileMime(fileName || '') + '\r\n\r\n'
} else {
dataString += '\r\n\r\n'
dataString += value
}
var dataArray = []
dataArray.push(...dataString.toUtf8Bytes())
if (isFile) {
let fileArray = new Uint8Array(value)
dataArray = dataArray.concat(Array.prototype.slice.call(fileArray))
}
dataArray.push(...'\r'.toUtf8Bytes())
dataArray.push(...'\n'.toUtf8Bytes())
return dataArray
}
function getFileMime(fileName: string) {
let idx = fileName.lastIndexOf('.')
let index: string = fileName.substr(idx)
let mime = mimeMap[index]
return mime ? mime : 'application/octet-stream'
}
function stringToUtf8(string: string) {
let encoder = new TextEncoder()
return encoder.encode(string)
}
String.prototype.toUtf8Bytes = function () {
var str: any = this
var bytes = []
for (var i = 0; i < str.length; i++) {
bytes.push(...str.utf8CodeAt(i))
if (str.codePointAt(i) > 0xffff) {
i++
}
}
return bytes
}
String.prototype.utf8CodeAt = function (i: any) {
var str = this
var out = [],
p = 0
var c = str.charCodeAt(i)
if (c < 128) {
out[p++] = c
} else if (c < 2048) {
out[p++] = (c >> 6) | 192
out[p++] = (c & 63) | 128
} else if ((c & 0xfc00) == 0xd800 && i + 1 < str.length && (str.charCodeAt(i + 1) & 0xfc00) == 0xdc00) {
// Surrogate Pair
c = 0x10000 + ((c & 0x03ff) << 10) + (str.charCodeAt(++i) & 0x03ff)
out[p++] = (c >> 18) | 240
out[p++] = ((c >> 12) & 63) | 128
out[p++] = ((c >> 6) & 63) | 128
out[p++] = (c & 63) | 128
} else {
out[p++] = (c >> 12) | 224
out[p++] = ((c >> 6) & 63) | 128
out[p++] = (c & 63) | 128
}
return out
}
export default FormData
mimeMap.ts
const mimeMap:{[key:string]:any = {
// 此处省略...
// 把一些重复的处理一下
}
formData.d.ts
interface String {
toUtf8Bytes(): any
utf8CodeAt(i:any): any
}
以上就是我的分享,有问题留言