uni.saveFile默认保存至应用私有目录,用户不可见;需用plus.downloader指定file:///绝对路径并申请存储权限,Android 10+须适配Scoped Storage及厂商限制。

uni.saveFile 保存的路径用户根本找不到
直接用 uni.downloadFile + uni.saveFile,文件确实“保存成功”了,但返回的 savedFilePath 多是类似 /Android/data/io.dcloud.HBuilder/apps/HBuilder/doc/uniapp_save/xxx.pdf 这种路径。它藏在应用私有目录里,普通用户进系统文件管理器翻十层都摸不到,更别说分享或二次打开。
- 这是设计使然:uni.saveFile 默认走沙箱机制,不申请存储权限也能运行,但代价是用户不可见
- Android 10+ 设备上,这个路径甚至可能被系统进一步隔离(Scoped Storage),
uni.openDocument都可能打不开 - 文件名大概率丢失原名,变成随机字符串,比如
8a3f2b1e.tmp,用户完全无法识别内容
plus.downloader 是绕过沙箱的唯一可靠方案
要真正把文件存到用户能随手点开的地方(比如 /storage/emulated/0/Download/ 或自定义的 /surveyApp/),必须用 HTML5+ 原生能力 plus.downloader.createDownload,它支持直接指定绝对路径,且 filename 参数可带完整路径和原始文件名。
- 路径必须以
file:///开头,例如:file:///storage/emulated/0/Download/report_2024.pdf - Android 10+ 必须先调用
plus.android.requestPermissions申请android.permission.WRITE_EXTERNAL_STORAGE(或适配分区存储,用Environment.getExternalStoragePublicDirectory) - iOS 上
filename只能是相对路径(如documents/report.pdf),最终位置由系统决定,但至少会进“文件”App 可见区域 - 别漏掉
plus.io.convertLocalFileSystemURL(d.filename)—— 否则日志里看到的是内部 URI,不是用户能理解的文件路径
文件名和路径拼接必须自己控制
后端返回的 Content-Disposition 或文件扩展名不能靠猜,也不能依赖 tempFilePath 的原始名(它可能已被截断或污染)。你得从响应头、URL 或接口参数里显式提取真实文件名,并拼进 filename 字段。
- 推荐做法:后端在接口返回中附带
filename字段,前端取值后做安全过滤(去掉../、非法字符) - 拼路径时用
plus.io.resolveLocalFileSystemURL或手动拼接,确保目标目录存在;不存在就用plus.io.mkdir创建(比如/surveyApp/) - 示例片段:
filename: "file:///storage/emulated/0/surveyApp/" + encodeURIComponent(realName),注意encodeURIComponent防止中文乱码
安卓 10+ 权限和路径适配是最大雷区
很多开发者测试机是 Android 9,一切正常;一上 Android 11 就报错或静默失败——问题几乎全出在权限和路径写法上。
- Android 10+ 不再允许直接写
/storage/emulated/0/,需改用plus.android.getExternalStorageDirectory()获取真实路径(返回类似/sdcard/或/storage/emulated/0/) - 即使有了路径,也得检查是否已授权:用
plus.android.hasPermission("android.permission.WRITE_EXTERNAL_STORAGE")判断,未授权就主动弹窗请求 - 部分厂商(如小米、华为)还有额外的“应用自启动”“文件访问管理”开关,系统级拦截比权限还狠,得在提示文案里明确告诉用户去哪关

















