uni.downloadfile 无法获取实时进度是因为其 onprogressupdate 仅 h5 原生支持,app 端需用 plus.downloader.createdownload,小程序需按平台适配;各端进度计算、更新频率和异常处理逻辑差异大,需分别实现。

uni.downloadFile 为什么拿不到实时进度
因为 uni.downloadFile 默认只在开始和结束时触发回调,不提供中间进度事件——这不是 bug,是设计如此。想显示进度条,必须换思路。
常见错误现象:onProgressUpdate 回调始终不执行,或只在安卓上有效、iOS 完全没反应;有人强行用定时器轮询临时文件大小,结果路径不可读、报错 file system error。
- 真正能拿到实时进度的,只有
uni.downloadFile的onProgressUpdate,但它**仅在 H5 平台原生支持**,App(iOS/Android)和小程序平台需走兼容方案 - App 端必须用
plus.downloader.createDownload(5+ SDK),它有完整的onstatechanged和onprogress回调 - 微信小程序要用
wx.downloadFile+onProgressUpdate(基础库 2.7.0+),但注意:它只在真机生效,开发者工具不触发
App 端用 plus.downloader 实现可监听进度的下载
这是目前 App(iOS/Android)唯一稳定获取下载进度的方式,依赖 5+ Runtime,不能在 H5 或纯微信环境用。
使用场景:你打包的是 uni-app App(非 H5 或小程序),且已开启「使用原生渲染」或默认启用 5+ 环境。
-
plus.downloader.createDownload返回一个downloadTask对象,调用start()后,onprogress每秒触发约 1–3 次,含loaded和total - 别直接用
loaded / total * 100算百分比——total初期可能为 0(服务器未返回 Content-Length),得加判断:if (total > 0) { percent = Math.round(loaded / total * 100) } - 下载完成时状态码为
200,但失败不一定是 HTTP 错误码,也可能是state === 4(CANCELLED)、state === 5(ERROR),需在onstatechanged中区分处理
const dtask = plus.downloader.createDownload(url, { filename: '_doc/download/test.pdf' });
dtask.onprogress = (res) => {
if (res.total > 0) {
this.progress = Math.round(res.loaded / res.total * 100);
}
};
dtask.start();
H5 和小程序怎么统一写法又不翻车
不能靠一套代码通吃所有端。H5 用 uni.downloadFile + onProgressUpdate,小程序各自适配,App 单独分支——这是现实约束下的最优解。
参数差异明显:uni.downloadFile 的 onProgressUpdate 在 H5 返回 { progress, totalBytesWritten, totalBytesExpectedToWrite };微信小程序返回字段名一样但单位是字节,且 progress 是整数(0–100);而 plus.downloader 的 onprogress 没有 progress 字段,全靠算。
- 别在
onProgressUpdate里频繁更新 Vue 响应式数据(如this.progress = ...),容易卡顿,建议加防抖(setTimeout节流到 300ms 内最多更新一次) - 微信小程序中,如果服务器响应头缺失
Content-Length,totalBytesExpectedToWrite会是 -1,此时进度条只能显示“正在下载…”文字,无法计算百分比 - H5 下
uni.downloadFile不支持断点续传,App 端plus.downloader支持,但需手动管理filename和恢复逻辑,否则重复下载会覆盖
进度条 UI 更新卡顿或跳变怎么办
不是代码写错了,大概率是更新频率太高 + Vue 响应式开销叠加导致的视觉异常。尤其在低端安卓机或长文件下载时明显。
- 避免每次
onprogress都触发setData或赋值响应式变量;用requestAnimationFrame或简单setTimeout聚合更新,比如“每 200ms 最多更新一次” - 不要用
v-bind:style="{ width: progress + '%' }"直接绑宽度——CSS 动画会失效,改用transform: scaleX()或配合transition类更平滑 - 下载完成前,
progress到 99% 就停住?检查是否漏了onHeadersReceived或服务端分块传输(chunked)导致total一直不准,这时建议隐藏数字、只用 loading 动画过渡最后阶段
实际做下来,最麻烦的从来不是写几行进度监听,而是各端对「文件长度未知」和「连接中断重试」的处理逻辑不一致。尤其是 App 端 resume 下载后,loaded 是从断点开始计,但 total 还是原始值——这点很容易被忽略。










