解决uni-app通用上传与后端接口不匹配问题:原生上传文件方法封装 ✨
在 uni-app 开发中,文件上传是高频需求 📤。 uni-app自带的uni.uploadFile虽便捷,但面对后端接口的个性化要求时,常因参数格式、请求头限制等问题 “卡壳”。今天就分享一个我封装的原生上传方法,完美解决接口不匹配问题,还附带详细使用指南哦!

一、方法背景 📝
最近项目中,后端接口提出了 3 个 “特殊要求”:
-
业务数据需以
Blob类型的 JSON 格式 传递 📦; -
请求头必须携带自定义的
RequestId加密字段 🔑; -
需实时展示文件上传进度 📊。
而uni.uploadFile在「参数格式转换」和「自定义请求头」上存在局限,无法直接满足需求。因此,基于XMLHttpRequest和FormData封装了这个原生上传方法,一次性解决所有痛点!
二、封装的上传文件方法代码 🚀
// 引入基础地址和获取加密RequestId的方法import { BASE_URL } from '@/config/requestConfig';import { getEncrypted } from '@/utils/encryptionUtil';// 自定义文件上传方法(解决uni.uploadFile接口不匹配问题)// @param {Object} formDatas - 业务相关的参数数据(如作品名称、作者信息)// @param {Array} files - 待上传文件数组(每个文件需含name和data属性)// @returns {Promise} - 成功resolve结果,失败reject错误export const upLoadWorks = (formDatas, files) => {return new Promise(async (resolve, reject) => {// 1. 拼接完整上传接口地址(需替换为你的实际接口)const url = BASE_URL + '/auth/upload';// 2. 创建FormData承载参数和文件(核心容器)const formData = new FormData();// 🌟 关键:将业务参数转为Blob类型JSON(后端要求的格式)formData.append('teamWorksCustom', new Blob([JSON.stringify(formDatas)], {type: "application/json"}));// 🌟 关键:遍历文件数组,添加到FormData(支持多文件上传)files.map(file => {formData.append(file.name, file.data);});// 3. 初始化XMLHttpRequest(原生AJAX核心)const xhr = new XMLHttpRequest();xhr.open('POST', url, true); // 异步POST请求// 🌟 关键:设置自定义请求头(后端要求的RequestId)xhr.setRequestHeader('RequestId', await getEncrypted());// 🌟 关键:监听上传进度(实时展示给用户)xhr.upload.onprogress = function(event) {if (event.lengthComputable) {// 计算上传进度百分比let percentComplete = event.loaded / event.total;// 显示进度loading(提升用户体验)uni.showLoading({title: '已上传' + Math.floor((percentComplete * 100)) + '%',mask: true});} else {console.log('无法计算进度 📈');}};// 4. 处理请求结果xhr.onload = function() {uni.hideLoading(); // 关闭loading(无论成败都要关!)if (xhr.status === 200) {// 成功:解析JSON响应并返回resolve(JSON.parse(xhr.responseText));} else {// 失败:返回状态码(便于排查问题)reject(xhr.status);}};// 5. 处理请求错误(如网络异常)xhr.onerror = function(err) {uni.hideLoading();reject(err);console.error('请求出错 ❌', err);};// 6. 发送请求(核心步骤)xhr.send(formData);});};
三、方法解析 🔍
1. 核心参数说明
| 参数名 | 类型 | 作用 | 注意事项 |
|---|---|---|---|
formDatas | Object | 业务参数(如作品名、作者) | 必须转为Blob类型 JSON,否则后端无法解析! |
files | Array | 待上传文件数组 | 每个文件需含 name(后端字段名) 和 data(文件原始数据) |
2. 3 个核心逻辑(必看!)
-
📦 FormData 构建:用
FormData统一承载 “Blob 类型业务参数” 和 “文件数据”,契合后端接口的参数格式要求; -
🔑 自定义请求头:通过
xhr.setRequestHeader添加RequestId,解决uni.uploadFile无法灵活设置请求头的问题; -
📊 进度监听:
xhr.upload.onprogress实时计算进度,搭配uni.showLoading让用户清晰感知上传状态,避免 “卡顿时误以为没反应”。
四、使用场景(精准匹配!)🎯
以下场景用这个方法,效率直接拉满:
-
后端要求特殊参数格式 📋:比如业务参数需
Blob类型 JSON、多参数嵌套等,uni.uploadFile搞不定的情况; -
需要自定义请求头 🔒:比如接口要求携带
Token、RequestId等自定义字段,且需加密处理; -
大文件上传需进度展示 📁:如视频、压缩包等大文件,进度条能极大提升用户体验;
-
uni.uploadFile无法满足的其他场景 ❌:比如后端接口是 RESTful 风格、需自定义响应解析逻辑等。
五、适用的后台接口类型 🖥️
你的后端接口需满足以下 4 个条件,才能完美适配这个方法:
-
✅ 支持FormData 格式请求体:能解析
FormData中的 “Blob 参数” 和 “文件数据”; -
✅ 能识别自定义请求头:能读取并验证
RequestId等自定义字段; -
✅ 返回JSON 格式响应:方法会自动解析 JSON,若后端返回其他格式(如 XML)需修改
resolve逻辑; -
✅ 支持多文件上传:能根据
file.name(前端传的字段名)分别接收多个文件。
六、使用步骤(手把手教学!)📝
1. 准备依赖(先做这步!)
确保项目中已引入 2 个关键依赖:
-
BASE_URL:接口基础地址(如https://api.xxx.com); -
getEncrypted():异步获取加密RequestId的方法(若后端不需要,可删除请求头相关代码)。
2. 3 步调用上传方法
(1)选择文件并转成 “文件数据”
用 Uniapp 的uni.chooseImage/uni.chooseVideo选文件,再通过uni.getFileSystemManager()将 “临时路径” 转成 “原始文件数据”:
// 选择图片示例(支持多选)uni.chooseImage({count: 3, // 最多选3张sizeType: ['original', 'compressed'], // 原图/压缩图sourceType: ['album', 'camera'], // 相册/相机success: async (res) => {const tempFilePaths = res.tempFilePaths;const files = [];// 遍历临时路径,转成文件数据for (let i = 0; i < tempFilePaths.length; i++) {const tempFilePath = tempFilePaths[i];// 读取文件数据(关键步骤!)const fileData = await new Promise((resolve) => {uni.getFileSystemManager().readFile({filePath: tempFilePath,success: (fileRes) => {resolve(fileRes.data); // 拿到原始文件数据}});});// 添加到files数组(name要和后端字段名一致!)files.push({name: `image${i + 1}`, // 后端接收字段名,如image1、image2data: fileData});}// 调用上传方法(下一步讲)await uploadFiles(files);}});
(2)准备业务参数
// 示例:业务参数(根据你的实际需求修改)const formDatas = {workName: '我的旅行vlog', // 作品名author: '小明', // 作者teamId: 'team_123456', // 团队ID// 其他参数...};
(3)调用方法并处理结果
import { upLoadWorks } from '@/utils/uploadUtil'; // 引入方法async function uploadFiles(files) {try {const formDatas = { workName: '我的旅行vlog', author: '小明' };// 调用上传方法const uploadResult = await upLoadWorks(formDatas, files);// 成功:提示+后续操作(如跳转页面)uni.showToast({ title: '上传成功 ✅', icon: 'success' });console.log('上传结果:', uploadResult);// 跳转页面示例:uni.navigateTo({ url: '/pages/success/success' });} catch (err) {// 失败:提示用户+记录错误(便于排查)uni.showToast({ title: '上传失败 ❌', icon: 'none' });console.error('上传错误:', err);}}
七、注意事项(避坑指南!)⚠️
-
📁 文件数据必须转对:如果直接传 “临时文件路径”(如
res.tempFilePaths),会导致上传失败!必须用uni.getFileSystemManager().readFile转成原始文件数据; -
🔑 RequestId 要有效:
getEncrypted()必须返回后端认可的加密值,否则会被接口拦截(若不需要,直接删除xhr.setRequestHeader这行); -
📍 接口地址要改对:
url = BASE_URL + '/auth/upload'中的/auth/upload是示例接口,一定要替换成你的实际接口路径; -
📱 兼容性测试:该方法用了
XMLHttpRequest和FormData,在 Uniapp 的 H5、App 端基本没问题,但低版本小程序可能有兼容问题,上线前务必多端测试; -
🚨 loading 必须关闭:
xhr.onload和xhr.onerror中都要加uni.hideLoading(),否则会出现 “loading 一直转” 的尴尬情况!
八、总结 📌
这个原生上传方法的核心价值,在于突破了 Uniapp 通用方法的限制,精准匹配后端接口的个性化需求。无论是 “特殊参数格式”“自定义请求头”,还是 “进度展示”,都能一站式解决。
实际开发中,你可以根据后端接口的变化灵活调整:比如不需要RequestId就删请求头代码,后端返回非 JSON 就修改resolve的解析逻辑。希望这个方法能帮你少踩坑,高效完成文件上传功能!
如果有疑问或优化建议,欢迎在评论区交流哦~ 💬
