并发超限会导致小程序上传任务静默丢弃,必须通过队列+并发控制(如MAX_CONCURRENCY=3)实现稳定上传,关键在于绑定原始索引、在complete中递归调度、手动管理中断与重试。

uni.uploadFile并发数超限会直接丢任务
小程序端(尤其是微信)对活跃的 uploadTask 有硬性限制,通常为 3–5 个。超出后新调用的 uni.uploadFile 不会报错,也不会触发 success 或 fail,而是静默排队甚至被丢弃——你看到的“卡在 0%”或“部分图片没回调”,基本就是这个原因。
App 端虽无明确上限,但并发太多会吃光内存、触发系统限流;H5 端则受限于浏览器并发连接数(一般 6–8 个)。所以「排队等待」不是可选项,是必须做的兜底控制。
用队列 + 并发数控制实现稳定上传
核心思路:维护一个待上传队列,只允许最多 N 个任务同时运行,每完成一个就从队列取下一个补位。不能靠 Promise.all,它会一次性发起全部请求,等于没控。
- 定义最大并发数,比如
const MAX_CONCURRENCY = 3 - 用数组存所有待上传路径:
this.pendingList = [...this.filePaths] - 写一个递归启动函数,每次只启动不超过
MAX_CONCURRENCY个任务,并在每个任务的complete回调里触发下一轮调度 - 别用
for...of+await顺序执行——太慢;也别用Promise.all——压根不排队
示例关键逻辑:
startUploadQueue() {
const MAX_CONCURRENCY = 3;
let running = 0;
const uploadNext = () => {
if (this.pendingList.length === 0 && running === 0) return;
while (running < MAX_CONCURRENCY && this.pendingList.length > 0) {
const filePath = this.pendingList.shift();
running++;
uni.uploadFile({
url: 'https://api.example.com/upload',
filePath,
name: 'file',
success: (res) => {
// 处理单张成功
},
fail: (err) => {
// 记录失败,可重试或跳过
},
complete: () => {
running--;
uploadNext(); // 触发下一轮
}
});
}
};
uploadNext();
}
队列中断和重试必须手动接管
原生 uni.uploadFile 没有暂停/取消能力,所谓「中断」只能是:不再往队列塞新任务 + 等正在跑的任务自然结束。如果用户点了取消,你需要:
- 清空
pendingList,防止后续补位 - 对已发出但未完成的任务,无法 abort,只能等它自己结束或超时
- 想支持真正取消,得用
uni.uploadFile返回的task对象调abort(),但注意:iOS 小程序不支持该方法,调了也没反应 - 重试建议放在
fail里做有限次数重试(如 2 次),并把失败项重新 unshift 回pendingList开头,避免顺序乱掉
进度更新容易错位,必须绑定原始索引
队列模式下,上传顺序 ≠ 原始选择顺序,而 UI 层需要按用户选图顺序显示进度。最稳妥做法是:在入队时就给每个任务打上原始序号,而不是靠回调时机推断。
错误写法:results[i] = res —— i 是队列位置,不是原始位置
正确写法:入队时带上下标
const taskItem = {
filePath: filePath,
index: originalIndex // 来自 chooseImage 的 tempFilePaths 下标
};
this.pendingList.push(taskItem);
// 在 success/fail 中用 taskItem.index 更新对应 UI 进度条
否则 iOS 和 Android 因底层调度差异,极大概率出现进度条“跳着走”或最终结果顺序错乱。
队列逻辑看着简单,但真正稳住的关键点不在“怎么排”,而在“怎么记原始位置”和“怎么收尾”。漏掉索引绑定或忽略complete 里的递归触发,上传就会变成不可预测的随机行为。


















