直接上代码:
/**
* 模拟 FormData,手动构造 multipart/form-data 请求
* @param {Object} params - 文本字段,如 { name: 'xxx', age: 18 }
* @param {Array} files - 文件列表,每个元素是 { name: 'file字段名', path: '小程序文件路径', type: '文件MIME类型' }
* @return {Object} - { buffer: ArrayBuffer, header: { 'Content-Type': ... } }
*/
export function createFormData(params = {}, files = [],token = "") {
// 生成唯一 boundary(可自定义更复杂规则)
const boundary = `----WebKitFormBoundary${Date.now()}${Math.random().toString(36).substr(2)}`;
const boundaryPrefix = `--${boundary}\r\n`;
const endBoundary = `--${boundary}--\r\n`;
let body = [];
// 1. 处理文本参数
Object.entries(params).forEach(([key, value]) => {
let part = `${boundaryPrefix}Content-Disposition: form-data; name="${key}"\r\n\r\n${value}\r\n`;
body.push(part);
});
// 2. 处理文件参数(多文件)
return new Promise((resolve, reject) => {
const filePromises = files.map(async (file) => {
try {
// 读取文件为 ArrayBuffer
const fileRes = wx.getFileSystemManager().readFileSync(
file.url
);
const fileData = new Uint8Array(fileRes);
let filename = file.url.match(/[^/]+$/)[0];
// 拼接文件部分的请求体 files 是后端文件接收的参数名
let fileParam = `${boundaryPrefix}Content-Disposition: form-data; name="files"; filename="${filename}"\r\nContent-Type: ${file.type}\r\n\r\n`;
body.push(
fileParam
);
body.push(fileData);
body.push(`\r\n`);
} catch (err) {
reject(err);
}
});
// 3. 所有文件处理完后,拼接最终请求体
Promise.all(filePromises)
.then(() => {
// 拼接结尾 boundary
body.push(endBoundary);
// 合并所有部分为 ArrayBuffer
const uint8Array = new Uint8Array(body.reduce((totalLength, part) => {
if (typeof part === 'string') {
// 字符串转 Uint8Array
const strBytes = new TextEncoder().encode(part);
totalLength += strBytes.length;
return totalLength;
} else {
// Uint8Array 直接累加长度
totalLength += part.length;
return totalLength;
}
}, 0));
let offset = 0;
body.forEach((part) => {
if (typeof part === 'string') {
const strBytes = new TextEncoder().encode(part);
uint8Array.set(strBytes, offset);
offset += strBytes.length;
} else {
uint8Array.set(part, offset);
offset += part.length;
}
});
let header = {
'Content-Type': `multipart/form-data; boundary=${boundary}`,
//添加请求体token
...(token && { Authorization: `Bearer ${token}` })
}
// 返回可直接用于 wx.request 的数据和请求头
resolve({
buffer: uint8Array.buffer,
header: header
});
})
.catch(reject);
});
}
创建formData并调用接口:
async submitComment() {
//参数数据各自业务传递
const formData = await createFormData({entity:JSON.stringify(form) },files,token);
wx.request({
url: url, // 后端上传接口
method: 'POST',
data: formData.buffer,
header: {
...formData.header,
// 若有 Token 等其他头信息,继续添加
// 'Authorization': `Bearer ${token}`
},
success(res) {
}
})
}
图例: