App端图片批量下载须用plus.downloader.createDownload并限制并发3–5个,通过extra绑定原始数据、convertLocalFileSystemURL转路径、statechanged事件监听状态,避免uni.downloadFile闭包问题导致路径错乱。

App端图片批量下载不能靠 for 循环 + uni.downloadFile 直接堆,否则路径全乱、进度无法绑定、失败后无重试、低端机卡死——这是实测踩过的坑。
为什么 for 循环批量调用 uni.downloadFile 会丢路径
因为 uni.downloadFile 是异步的,for 循环瞬间跑完,所有回调里的 data[i] 引用的都是循环结束后的最终 i 值(闭包问题)。结果就是:10 张图下载完,全部写进了 data[9].path,前 9 个对象的 path 字段仍是空或旧值。
- 必须用闭包固化当前索引,比如
(i => { /* 使用 i */ })(i)或直接改用forEach+ 箭头函数 - 更稳妥的做法是把每个文件的原始数据对象传进下载回调,而不是依赖外部变量索引
- 安卓真机上尤其明显,iOS 因 JSCore 优化稍好,但逻辑不可靠
plus.downloader.createDownload 是 App 端多任务并行的唯一可靠选择
uni.downloadFile 在 App 端本质是封装了 plus.downloader,但只暴露单任务接口;要真正并行、后台持续、任务可查,必须直连原生下载器。
- 每个任务需独立
taskId,推荐用 URL 的 hash 或时间戳生成,避免重复创建 -
filename参数必须带子目录,如'_downloads/img/xxx.jpg',否则可能被系统拒绝写入 - 监听
statechanged事件比轮询更省资源,状态码plus.downloader.DOWNLOADING/DONE/FAILED要逐个判断 - 并发数建议限制在 3–5 个,实测超过 5 个在低端 Android 上易触发系统限流,反而更慢
如何把下载路径正确回填到原始数据数组
关键不是“怎么存”,而是“怎么绑”——必须让下载完成回调能精准定位到它对应的数据项。
- 创建任务时,把原始数组项的引用或唯一
id存进createDownload的extra配置里(plus.downloader支持) - 回调中通过
d.extra.id查找原数组,再赋值data.find(x => x.id === d.extra.id).localPath = d.filename - 不要用
indexOf或临时索引变量,App 后台切前台时索引容易错位 - 保存路径要用
plus.io.convertLocalFileSystemURL(d.filename)转成标准路径,否则后续uni.openDocument等 API 会打不开
进度合并计算和 UI 更新频率怎么控
单文件进度(%)和整体完成率(已成功数 / 总数)是两回事,混在一起显示会误导用户。
- 整体进度建议只按“完成数量”算,不按字节数——因为图片大小差异大,小图秒下完、大图卡半天,百分比跳变剧烈,体验差
- 单文件进度更新频率设为
500ms一次,太密(如100ms)会导致 Vue 响应式频繁触发,页面卡顿 - 在
statechanged回调里判断task.state === plus.downloader.DOWNLOADING时才更新单文件进度,避免 DONE 后还刷 - UI 层用
v-show控制进度条显隐,别用v-if,防止反复销毁重建组件
真正难的不是写下载逻辑,而是让每个任务的状态、路径、错误原因,在页面退到后台、内存回收、网络中断后还能对得上——这要求所有任务 ID 和数据引用全程不可变,且状态变更必须走 Vuex/Pinia 持久化,不能只存在组件 data 里。











