uni.downloadFile+uni.saveFile在各端filePath语义不同:H5忽略filePath、返回blob URL;App需沙箱绝对路径如_doc/xxx.pdf;小程序须合法临时路径且不可跨会话复用,硬统一路径必崩。

多端文件路径不一致不是配置漏了,而是你默认把“路径”当成一个字符串在用,但 H5、App、小程序三端对 filePath 的语义理解完全不同——H5 里它是浏览器 Blob URL 或本地 File 对象,App 里它是沙箱内绝对路径,小程序里它又必须是临时路径或包内资源路径。硬写一个值去适配三端,必然崩。
uni.downloadFile + uni.saveFile 在各端的 filePath 含义差异
这是最常被忽略的底层逻辑:同一套下载代码,filePath 参数在不同平台代表完全不同的东西。
- H5:
filePath不生效(uni-app 会忽略),下载后返回的是tempFilePath,但这个值只是blob:http://...地址,不能直接用于上传或分享 - App(Android/iOS):
filePath必须是应用沙箱内的可写路径,比如_doc/xxx.pdf;若留空,系统自动分配临时路径,但该路径不可见、不可持久化 - 小程序:
filePath必须是合法的临时路径(来自uni.getFileSystemManager().joinPath或uni.downloadFile返回),且不能跨会话复用
所以别再全局定义一个 DOWNLOAD_DIR = '/downloads' 然后到处传——它在 H5 是非法字符串,在小程序会直接报错 fail filePath is not valid。
统一用 tempFilePath + 平台条件编译做中转
真正可行的路径处理方式,是放弃“统一路径”,改用“统一行为 + 分离存储”。核心思路:所有平台都先走 uni.downloadFile 拿到 tempFilePath,再按平台特性决定下一步。
- H5:不调
uni.saveFile,直接用fetch+blob触发浏览器原生下载:const blob = await fetch(res.tempFilePath).then(r => r.blob()); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = fileName; a.click(); URL.revokeObjectURL(url); - App:调用
uni.saveFile,但filePath显式指定为_doc/${fileName},确保用户能在文件管理器「我的文档」里找到 - 小程序:只保留
tempFilePath,配合uni.openDocument直接预览,不落地;如需保存,则用uni.saveFile+success回调里的savedFilePath,它才是可长期引用的路径
鸿蒙平台必须绕开 uni.saveFile,改用 DocumentViewPicker
鸿蒙(HarmonyOS)是特例中的特例:uni.saveFile 保存的文件仍在沙箱内,用户在「文件管理」里根本看不到。这不是 bug,是系统设计如此。
- 不要尝试拼接鸿蒙的私有路径(如
/data/storage/el2/base/...),它随版本变化且无文档保证 - 必须用 UTS 插件调用
picker.DocumentViewPicker,让用户自己选保存位置(如Downloads或Documents) - UTS 中需显式声明权限:
和 - 注意:该 API 仅鸿蒙可用,其他平台仍走原逻辑,必须用
/* #ifdef h5 || app || mp-weixin */条件编译隔离
最复杂的不是怎么写,而是意识到:所谓“路径一致”,本质是“用户感知一致”。H5 要浏览器下载弹窗,App 要文件管理器可见,小程序要即点即开,鸿蒙要用户自主选目录——它们本就不该共享同一个字符串变量。把路径当黑盒,用平台能力兜底,比强行统一更可靠。











