uni.downloadFile + uni.saveFile 无法将文件存至用户可访问目录,因默认保存至应用私有路径(Android:/Android/data/...;iOS:不可见区域),且不支持自定义路径、原始文件名及外部访问。

uni-app 的 uni.downloadFile + uni.saveFile 组合不能把文件存到用户能直接访问的目录,这是设计限制,不是你代码写错了。
为什么 savedFilePath 用户根本找不到
Android 上 uni.saveFile 返回的 savedFilePath 默认是应用私有路径,例如 /Android/data/io.dcloud.HBuilder/apps/HBuilder/doc/uniapp_save/xxx.pdf。这个路径在系统文件管理器里被隐藏,普通用户进不去,也无法被其他 App(包括微信、WPS)识别或调起。
- iOS 更严格:该路径不在“文件”App 可见区域,
uni.openDocument能打开,但用户无法手动找到或分享 - Android 10+(API 29+)启用 Scoped Storage 后,连
/storage/emulated/0/Download/这种传统路径都写不进去,除非降级或走原生 API - 文件名大概率丢失:
tempFilePath是随机字符串(如8a3f2b1e.tmp),uni.saveFile不解析原始文件名,也不会补后缀
plus.downloader 是唯一可靠方案(App端)
要真正写入 /Download/、/Documents/ 或自定义目录(如 /surveyApp/),必须用 HTML5+ 原生能力 plus.downloader.createDownload,它支持指定绝对路径和原始文件名。
- 路径格式必须是
file:///storage/emulated/0/Download/report.pdf(Android)或documents/report.pdf(iOS) - Android 需动态申请权限:
plus.android.requestPermissions(['android.permission.WRITE_EXTERNAL_STORAGE']) - iOS 不需要额外权限,但
filename只能是相对路径,最终位置由系统决定(不过会进“文件”App) - 别漏掉
plus.io.convertLocalFileSystemURL(d.filename),否则日志里看到的是内部 URI,不是用户可读路径 - 后端最好在响应中带
filename字段,前端做安全过滤(去掉../、控制字符),再拼进完整路径
uni.downloadFile + uni.saveFile 仅适用于“能接受私有路径”的场景
如果你只是想让用户点开预览,不强求他手动找文件,那这套组合仍可用,但必须注意几个硬性条件:
- 务必等
uni.saveFile成功回调后再取saveRes.savedFilePath,不要传res.tempFilePath给uni.openDocument - Android 上若后缀缺失(比如服务器返回无扩展名的 blob),需手动补全:
saveRes.savedFilePath + '.pdf' - 大文件慎用:临时文件可能被系统清理,且
uni.saveFile没有进度反馈,卡住时无法感知 - H5 端行为完全不同:它会触发浏览器原生下载,
uni.saveFile无效,不用处理路径
路径拼接与目录创建必须自己兜底
plus.downloader 不会自动创建不存在的父目录,比如你想存到 file:///storage/emulated/0/surveyApp/report.pdf,但 surveyApp 文件夹不存在,就会失败。
- 先用
plus.io.resolveLocalFileSystemURL('file:///storage/emulated/0/surveyApp/')判断目录是否存在 - 不存在就调用
plus.io.mkdir创建,注意传入完整路径并确保有写权限 - 中文文件名必须
encodeURIComponent编码,否则路径解析失败或乱码 - 不要依赖 URL 或响应头里的
Content-Disposition—— 它可能被 CDN、Nginx 或代理截断或改写
真正跨平台可控的文件落地,绕不开 plus.downloader;而它的路径构造、权限申请、目录初始化这三步,任意一步漏掉都会导致“看似成功、实则不可见”。


















