更新记录

1.0.1(2026-09-29) 下载此版本

  • 插件更名为 file-downloader,面向插件市场发布(功能与内部版本 lv-downloader 一致)
  • downloadToPublic 增加扩展名补全:保存文件名无扩展名时,优先取 URL 中的扩展名;两者都没有时兜底 .docx
  • 修复下载无扩展名接口(如 downfile?type=...、服务端 Content-Type 返回 text/html)导致落盘文件无后缀被识别为 txt 的问题

平台兼容性

uni-app(3.8.4)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
√ √ - - √ √ 6.0 12 -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - - -

其他

多语言 暗黑模式 宽屏模式 蒸汽模式
× × × √

file-downloader 文件下载(保存到系统公共目录)

下载网络文件(URL)并保存到系统公共目录,支持 Android / iOS 双端。文件不落隐藏目录、不落应用沙盒缓存,用户在系统「文件管理 / 文件 App」中可直接查看。

  • Android:保存到公共 Download 目录(Android 10+ 基于 MediaStore,免存储权限静默保存)
  • iOS:保存到 Documents 目录,保存成功后自动弹出系统分享面板,选「存储到文件」可存到任意位置

适配 uni-app Vue2(webpack 编译) 项目,同时兼容 Vue3。UTS 原生层(utssdk/)与 Vue 版本无关;JS 调用层为 ES5 风格,兼容 Vue2 老 babel 配置。

本插件基于 lvxiaowu 项目的 lv-downloader 1.2.1 重写并适配 Vue2,在此感谢原作者。

功能特性

  • 一行代码下载网络文件到系统公共目录,带实时进度回调
  • Android 10+ 免存储权限、无弹窗静默保存;Android 9 及以下自动申请存储权限
  • iOS 保存后自动弹系统分享面板(含「存储到文件 / AirDrop / 微信」等入口)
  • 支持自定义请求头(token 鉴权下载)、超时时间、公共目录下子目录
  • 自动解析/过滤文件名非法字符;无扩展名时自动补全(优先取 URL 扩展名,兜底 .docx)
  • 支持中断下载;支持把应用沙盒内已有本地文件转存到公共目录

环境要求

  • HBuilderX 3.6.5+
  • 必须使用自定义基座或正式打包(标准基座不含 UTS 插件):运行 → 运行到手机或模拟器 → 制作自定义调试基座

快速开始

import { downloadToPublic } from '@/uni_modules/file-downloader/js_sdk/index.js'

const { promise, abort } = downloadToPublic({
  url: 'https://example.com/files/report.docx',
  fileName: '检测报告.docx',                 // 可选,默认从 URL 提取
  headers: { Authorization: 'Bearer xxx' }, // 可选,鉴权下载
  subDir: 'myDocs',                          // 可选,公共目录下的子目录
  : function (progress, received, total) {
    console.log('进度 ' + progress + '%(' + received + '/' + total + ')')
  }
})

promise.then(function (res) {
  uni.showToast({ title: '已保存到:' + res.path, icon: 'none' })
}).catch(function (err) {
  uni.showToast({ title: err.message, icon: 'none' })
})

// 需要中断下载时
// abort()

把应用沙盒内已有的本地文件(缓存文件、生成的文件等)转存到公共目录:

import { saveFileToPublic } from '@/uni_modules/file-downloader/js_sdk/index.js'

saveFileToPublic('_doc/temp/report.pdf', '检测报告.pdf').then(function (res) {
  console.log('已保存到', res.path)
})

双端保存位置

平台 保存位置 用户如何看到 是否弹窗 权限
Android /storage/emulated/0/Download/<subDir>/ 文件管理 → 下载 否(静默) Android 10+ 免权限;Android 9- 自动申请存储权限
iOS 应用 Documents/<subDir>/ 保存成功后自动弹系统分享面板,选「存储到文件」可存到任意位置 保存后弹系统面板 无

iOS 系统没有全局公共下载目录,Apple 官方规定的对外公开位置就是 Documents(如需在「文件」App 中直接查看,需在 manifest.json 开启 UIFileSharingEnabled,见下文配置)。

API

downloadToPublic(options) → { promise, abort }

参数 类型 必填 说明
options.url String 是 网络文件地址
options.fileName String 否 保存文件名(含扩展名),默认从 URL 提取,自动过滤非法字符;无扩展名时自动补全:优先取 URL 扩展名,都没有则兜底 .docx
options.headers Object 否 附加请求头(token 鉴权下载)
options.subDir String 否 公共目录下的子目录,默认存根目录
options.timeout Number 否 下载超时毫秒,默认 60000
options.onProgress Function 否 进度回调 (progress, receivedBytes, totalBytes)
  • promise resolve 值:{ path: 公共目录最终路径, fileName: 文件名 }
  • abort() 中断下载,promise 会 reject「已取消下载」

saveFileToPublic(path, fileName, subDir) → Promise

把应用沙盒内的本地文件转存到公共目录,参数同上。

manifest.json 配置

Android 权限(Android 9 及以下兜底需要)

"app-plus": {
  "distribute": {
    "android": {
      "permissions": [
        "<uses-permission android:name=\"android.permission.INTERNET\"/>",
        "<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" android:maxSdkVersion=\"28\"/>"
      ]
    }
  }
}

Android 10+ 走 MediaStore,无需存储权限;maxSdkVersion=28 保证不会因此多弹权限申请。

⚠️ 注意:部分云打包服务会自动注入不带属性的 android.permission.WRITE_EXTERNAL_STORAGE(来自 App 运行时/已选模块/云原生插件)。若再手动声明带 maxSdkVersion="28" 的同名权限,Gradle Manifest Merger 可能因属性不一致报 duplicated 错误导致打包失败。遇到该错误时请移除手动声明,自动注入的声明已足够 Android 9 及以下的运行时权限申请。

iOS「文件」App 可见(可选)

"app-plus": {
  "distribute": {
    "ios": {
      "UIFileSharingEnabled": true,
      "LSSupportsOpeningDocumentsInPlace": true
    }
  }
}

配置后用户在系统「文件」App →「我的 iPhone」→ 应用名下即可直接看到下载的文件,也可通过 iTunes/AirDrop 分享。

实现原理

JS 层(js_sdk/index.js)                UTS 层(utssdk/*/index.uts)
┌─────────────────────────┐           ┌──────────────────────────────┐
│ uni.downloadFile 下载到  │  沙盒路径  │ saveFileToPublicDir:         │
│ 沙盒临时目录(带进度回调)│ ────────→  │  Android: MediaStore → Download│
│ 解析文件名/MIME          │           │  iOS: NSFileManager → Documents│
└─────────────────────────┘           └──────────────────────────────┘
  • Android:API 29+ 用 MediaStore.Downloads 插入记录并流式拷贝(512KB 缓冲,io 线程执行),文件秒级出现在系统「下载」;API 28 及以下直接写 Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS),运行时自动申请权限
  • iOS:FileManager 把文件从缓存目录拷到 Documents(同名覆盖);保存成功后 UTS 层直接弹 UIActivityViewController 系统分享面板(NSURL.fileURLWithPath 传真实文件),用户选「存储到文件」可存到任意位置。不走 plus.share.sendWithSystem——其 type:'file' 无法稳定识别文件类型,会退化成文本分享(存出内容为路径字符串的 txt)

Vue2 / Vue3 兼容说明

项目 说明
Vue 版本 Vue2(webpack 编译)与 Vue3(vite 编译)均支持
条件编译宏 #ifdef APP-PLUS(Vue2/webpack 老编译器可靠识别)
js_sdk 语法 ES5 风格 function + var(无默认参数、可选链),兼容 Vue2 老 babel 配置
HarmonyOS 不支持(UTS 原生层仅 Android / iOS)

错误码

code 平台 说明
2000 Android 写入异常(IO/权限)
2001-2003 Android 权限申请失败
3001 iOS 读取沙盒源文件失败
3002 iOS 写入 Documents 失败

常见问题

  1. 点了没反应 / 提示「当前环境不支持」:用的是标准基座。UTS 插件必须自定义基座或正式打包。
  2. iOS 保存后弹出的系统面板:保存到 Documents 成功后自动弹出,选「存储到文件」即可存到任意位置(我的 iPhone 任意文件夹 / iCloud)。若在面板中取消,文件仍保留在 App 的 Documents 中——需打包时启用 UIFileSharingEnabled 才会在「文件 App → 我的 iPhone → 应用名」中显示。
  3. Android 9 及以下保存失败:检查 manifest 权限声明(含 maxSdkVersion="28"),并确认用户在权限弹窗中点了允许。
  4. 同名文件:双端均直接覆盖同名旧文件。
  5. 小程序端 / H5 端:不支持,调用会 reject「当前环境不支持保存到公共目录」,请自行用各自平台的保存方案。
  6. 文件无扩展名:fileName 未传扩展名时会自动补全——优先取 URL 中的扩展名,都没有则兜底 .docx;建议调用时显式传入完整文件名。

目录结构

uni_modules/file-downloader
├── package.json
├── readme.md
├── changelog.md
├── js_sdk/index.js               # JS 调用层:下载编排 + 进度 + 文件名解析(Vue2 适配)
└── utssdk
    ├── interface.uts             # 跨平台类型定义
    ├── app-android/index.uts     # Android:MediaStore / 直接写入
    └── app-ios/index.uts         # iOS:NSFileManager → Documents

隐私、权限声明

1. 本插件需要申请的系统权限列表:

Android 9 及以下自动申请 WRITE_EXTERNAL_STORAGE;Android 10+ 无需任何存储权限

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

无

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

许可协议

MIT协议

暂无用户评论。