更新记录
1.2.1(2026-10-15)
- 新增整目录双向导入导出,保留子目录与空目录,支持同名文件跳过、记录失败或自动改名,以及逐项结果。
- 新增可取消的递归扫描与统计,支持分批条目、文件和目录数量、未知文件大小提示及目标同名预检。
- 新增目录任务进度和取消,扫描阶段超限或失败时不会写入目标,取消后可查看已处理结果与未处理数量。
- 修复 Android 整目录导出在重新访问目标目录后无法创建文件或子目录的问题。
- 修复 Android/iOS 单文件与批量复制使用框架沙盒路径时被拒绝的问题,可直接使用
uni.env.USER_DATA_PATH等目录返回的路径。 - 修正同名策略说明:fail 记录该项失败后继续处理其他项;已有单文件与批量调用方式保持。
- Android 本地递归扫描及目录导出需 Android 8.0 及以上;外置目录扫描、目录导入与旧能力保持原系统支持范围。原生代码更新后需重新制作对应平台自定义基座或重新打包 App。
1.2.0(2026-10-02)
- 新增 iOS U 盘和移动硬盘文件管理,可通过系统“文件”选择目录,完成文件浏览、读写、新建、删除和重命名。
- 新增 iOS 单个与批量导入导出,支持进度、取消任务以及同名文件跳过、记录失败或自动改名。
- 新增 iOS 目录授权记忆,应用重新打开后可继续使用仍然有效的已授权目录;设备断开时返回统一提示。
- 修复 iOS 云打包编译失败的问题;无需修改项目配置或调用代码,需要重新制作 iOS 自定义基座或重新打包 App。
- 修复 iOS 查询已授权目录或浏览目录时 App 可能闪退的问题;无需修改调用代码,升级后需要重新制作并安装 iOS 自定义基座或重新打包 App。
- Android 已有调用方式和文件操作保持不变。升级后无需修改调用代码;需要重新制作对应平台自定义基座或重新打包 App。
1.1.0(2026-09-23)
- 新增批量导入和批量导出,可一次提交多个文件,查看当前文件、整体数量和总字节进度,并在需要时取消整个任务。
- 新增同名文件处理方式,可选择跳过、记录失败或自动生成新文件名;已有文件不会被覆盖。
- 修复 U 盘或移动硬盘拔出后继续访问已授权目录时显示“未知错误”的问题;现在会提示设备已断开或介质已卸载。
- 修复项目包含插件时 iOS 云打包失败的问题;无需修改调用代码,需重新制作 iOS 自定义基座或重新打包 App。iOS 仍不支持 USB 存储读写。
- 修复部分 Android 设备可能错误显示“可以查询存储设备”的问题。
- 修复存储设备状态显示不准确的问题,现在可以正确区分正常连接、只读和其他状态。
- 完善能力与授权、文件与目录、文件读写、单个与批量复制、连接状态等模块的中文注释示例。
- 已有单文件调用无需修改;升级后需要重新制作并安装 Android 自定义基座或重新打包 App。
平台兼容性
uni-app(5.24)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| - | - | × | × | - | - | √ | √ | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | - | - | - | - | - | - | - | - | - | - |
uni-app x(5.24)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| × | × | √ | √ | × | × |
其他
| 多语言 | 暗黑模式 | 宽屏模式 | 蒸汽模式 |
|---|---|---|---|
| × | × | × | √ |
lizhao-usb-storage
介绍
lizhao-usb-storage 让 Android 和 iOS 应用可以在使用者选择目录后,浏览、读取和写入 U 盘、移动硬盘中的文件。它适合现场资料交换、离线导入导出和移动备份,可同时用于 uni-app 与 uni-app x。
功能特色
| 特色功能 | 能解决什么问题 | 相关 API |
|---|---|---|
| 文件浏览与整理 | 浏览文件和文件夹,查看大小和修改时间,创建、重命名或删除目录内容 | listEntries、statEntry、createDirectory、renameEntry、deleteEntry |
| 文本与小文件读写 | 读取配置、日志等文本,也可读写小型二进制文件 | readTextFile、writeTextFile、readFile、writeFile |
| 双向复制 | 把应用文件导出到 U 盘,也可以把 U 盘文件导入应用目录 | copyToStorage、copyFromStorage |
| 批量导入导出 | 一次处理多份照片、表格、压缩包或业务资料 | copyBatchToStorage、copyBatchFromStorage |
| 整目录导入导出 | 保留子文件夹和空文件夹,免去自行展开文件清单 | copyDirectoryToStorage、copyDirectoryFromStorage |
| 扫描与统计 | 分批查看目录内容、文件数量、大小及目标同名情况 | scanDirectory |
| 进度与取消 | 展示单个文件和整批文件的处理进度,并允许用户中途取消 | onProgress、cancelTask、cancelBatchTask |
| 同名文件保护 | 遇到同名文件时可跳过、记录失败或自动改名,避免误覆盖已有资料 | conflictStrategy |
| 记住已选目录 | 再次打开应用时恢复仍有效的目录,减少重复选择 | listStorages |
| 连接状态提示 | Android 可查看系统识别到的设备;Android 和 iOS 都能在授权或访问状态变化时更新提示 | listMountedVolumes、onStorageEvent |
适合哪些场景
| 使用场景 | 推荐功能 | 可以完成什么 |
|---|---|---|
| 现场采集后导出 | copyToStorage、copyBatchToStorage |
把应用内生成的一份或多份照片、表格、压缩包导出到 U 盘。 |
| 离线资料导入 | copyFromStorage、copyBatchFromStorage |
从 U 盘读取一份或多份资料并复制到应用内继续处理。 |
| 移动硬盘备份 | listEntries、copyBatchToStorage |
浏览目录并把多份应用资料一次备份到移动硬盘。 |
| 文本配置交换 | writeTextFile、readTextFile |
保存和读取配置、日志、清单等文本。 |
| 文件整理 | createDirectory、renameEntry、deleteEntry |
创建文件夹、重命名文件、删除不需要的内容。 |
首次使用时,需要在系统文件选择器中选择允许访问的文件夹;iOS 会打开“文件”选择界面。插件只操作使用者明确选择的目录。
下载与导入
- 打开 插件市场页面,选择“使用 HBuilderX 导入插件”,导入到 uni-app 或 uni-app x 项目。
- 保留完整的
uni_modules/lizhao-usb-storage目录,不要修改插件目录名。 - 在页面脚本中从插件根目录导入:
import {
getCapabilities,
pickStorageRoot,
listEntries,
writeTextFile,
readTextFile
} from '@/uni_modules/lizhao-usb-storage'
完整示例位于插件目录的 example/ 下:
- uni-app:
example/uniapp/index.vue - uni-app x:
example/uniappx/index.uvue
5 分钟跑通:选择目录并读写文件
先完成“查询能力 → 选择目录 → 文件操作”这三个步骤,再按业务需要添加复制、进度和状态监听。
1. 导入插件并查询能力
import {
getCapabilities,
pickStorageRoot,
listEntries,
writeTextFile,
readTextFile
} from '@/uni_modules/lizhao-usb-storage'
getCapabilities({
success: (res) => {
console.log('USB 存储能力', res)
},
fail: (err) => {
console.error('查询能力失败', err.errCode, err.errMsg)
}
})
2. 让用户选择外置存储目录
let storageId = ''
pickStorageRoot({
title: '请选择 U 盘或移动硬盘目录',
success: (storage) => {
storageId = storage.storageId
console.log('已取得存储授权', storage.displayName)
},
fail: (err) => {
console.error('选择目录失败', err.errCode, err.errMsg)
}
})
用户取消选择会返回 9036005。目录授权失效、系统拒绝访问或存储设备断开时,请提示用户重新连接设备并选择可访问的文件夹。
3. 写入并读回文本
writeTextFile({
storageId,
path: '/lizhao-demo/hello.txt',
text: '你好,USB 存储!',
encoding: 'utf-8',
createParents: true,
success: () => {
readTextFile({
storageId,
path: '/lizhao-demo/hello.txt',
success: (res) => console.log('读回内容', res.text),
fail: (err) => console.error('读取失败', err.errCode, err.errMsg)
})
},
fail: (err) => console.error('写入失败', err.errCode, err.errMsg)
})
所有异步 API 均使用 success、fail、complete 回调。失败时不会调用 success;complete 最多调用一次。
核心配置
先掌握下面几个常用参数即可完成大多数接入,其他参数可在后文“参数说明”中按需查询。
| 配置 | 默认值 | 什么时候使用 |
|---|---|---|
storageId |
无 | 选择目录成功后保存,后续浏览、读写和复制都需要传入。 |
path |
/ |
指定已选目录内的文件或文件夹;/ 表示所选目录本身。 |
createParents |
false |
目标上级目录可能不存在时设为 true。 |
overwrite |
false |
是否请求覆盖同名文件;写入外置存储时建议保持默认值。 |
maxBytes |
由调用方设置 | 读取小文件时限制最大字节数,防止一次加载过多内容。 |
conflictStrategy |
skip |
批量或目录复制遇到同名文件时,选择跳过、记录失败或自动改名。 |
接入方式选择
| 你的需求 | 推荐入口 | 阅读位置 |
|---|---|---|
| 先确认当前设备是否可用 | getCapabilities |
模块一:能力与授权 |
| 浏览和管理 U 盘文件 | listEntries、statEntry、createDirectory |
模块二:文件与目录 |
| 读取或保存文本、二进制文件 | readTextFile、writeTextFile、readFile、writeFile |
模块三:文件读写 |
| 导入导出单个或多个大文件 | copyFromStorage、copyToStorage、批量复制接口 |
模块四:双向复制 |
| 统计或导入导出整个文件夹 | scanDirectory、目录复制接口 |
模块五:整目录处理 |
| 监听 U 盘插拔和释放资源 | onStorageEvent、releaseResources |
模块六:连接状态 |
从基础到进阶:按业务模块使用
模块一:能力与授权
getCapabilities 只查询当前平台是否支持;pickStorageRoot 打开系统文件选择器,让使用者选择 U 盘或移动硬盘中的目录。选择成功后保存返回的 storageId,后续所有文件操作都使用它。
下面的示例会先检查当前运行环境,再让使用者选择目录;选择成功后把目录标识保存下来,后续调用都使用这个标识。
import {
getCapabilities,
pickStorageRoot,
listStorages
} from '@/uni_modules/lizhao-usb-storage'
let storageId = ''
// 进入页面时调用:先确认当前设备支持外置存储,再打开目录选择器。
function chooseUsbFolder() {
getCapabilities({
success: (capabilities) => {
if (!capabilities.supported) {
console.log('当前设备暂不支持 USB 存储')
return
}
pickStorageRoot({
title: '请选择 U 盘或移动硬盘目录',
success: (storage) => {
// storageId 是后续读写文件时使用的目录标识,请保存到页面状态。
storageId = storage.storageId
console.log('目录授权成功:', storage.displayName)
},
fail: (err) => {
console.error('选择目录失败:', err.errCode, err.errMsg)
}
})
},
fail: (err) => {
console.error('查询设备能力失败:', err.errCode, err.errMsg)
}
})
}
// 应用重新打开时,可以恢复之前仍然有效的目录授权。
function restoreUsbFolder() {
listStorages({
success: (storages) => {
if (storages.length > 0) {
storageId = storages[0].storageId
console.log('已恢复目录授权:', storages[0].displayName)
} else {
console.log('还没有可恢复的目录,请先选择目录')
}
},
fail: (err) => {
console.error('恢复目录授权失败:', err.errCode, err.errMsg)
}
})
}
模块二:文件与目录
使用 listEntries 浏览目录,使用 statEntry 查看文件信息,使用 createDirectory、renameEntry 和 deleteEntry 完成整理。传入的路径只能是已授权目录内的相对路径,根目录不能删除。
下面的示例展示进入目录、查看条目详情和整理文件夹。path 是所选授权目录内的相对路径,不能填写手机的完整系统路径。
import {
listEntries,
statEntry,
createDirectory,
renameEntry,
deleteEntry
} from '@/uni_modules/lizhao-usb-storage'
// storageId 应来自 pickStorageRoot 或 listStorages 的成功回调。
const storageId = '请替换为已取得的 storageId'
// 浏览授权目录当前层级的文件和文件夹。
function loadFolder() {
listEntries({
storageId,
path: '/',
success: (entries) => {
entries.forEach((entry) => {
console.log(entry.kind === 'directory' ? '文件夹:' : '文件:', entry.name)
})
},
fail: (err) => {
console.error('浏览目录失败:', err.errCode, err.errMsg)
}
})
}
// 查看某个文件或文件夹的大小、修改时间等信息。
function showEntryInfo() {
statEntry({
storageId,
path: '/reports/today.txt',
success: (entry) => {
console.log('文件大小:', entry.size, '字节')
console.log('最后修改时间:', entry.modifiedAt)
},
fail: (err) => {
console.error('读取条目信息失败:', err.errCode, err.errMsg)
}
})
}
// 创建多层文件夹,然后把它改名。
function organizeFolder() {
createDirectory({
storageId,
path: '/reports/2026',
recursive: true,
success: () => {
renameEntry({
storageId,
path: '/reports/2026',
name: 'year-2026',
success: () => console.log('文件夹整理完成'),
fail: (err) => console.error('重命名失败:', err.errCode, err.errMsg)
})
},
fail: (err) => {
console.error('创建文件夹失败:', err.errCode, err.errMsg)
}
})
}
// 删除前请先在页面上向使用者确认;根目录不能删除。
function removeUnusedFolder() {
deleteEntry({
storageId,
path: '/reports/temp',
recursive: true,
success: () => console.log('临时文件夹已删除'),
fail: (err) => console.error('删除失败:', err.errCode, err.errMsg)
})
}
模块三:文件读写
文本文件优先使用 readTextFile 和 writeTextFile;小型二进制文件使用 readFile 和 writeFile。写入同名文件时默认失败并保留原文件,避免误覆盖已有资料。
文本配置、日志和清单可以直接使用字符串读写;读取图片等二进制资料时,请设置合理的 maxBytes,大文件建议使用模块四的复制功能。
import {
writeTextFile,
readTextFile,
readFile,
writeFile
} from '@/uni_modules/lizhao-usb-storage'
const storageId = '请替换为已取得的 storageId'
// 保存文本时默认不会覆盖同名文件;需要覆盖时请先确认文件名和业务逻辑。
function saveConfig() {
writeTextFile({
storageId,
path: '/reports/config.json',
text: JSON.stringify({ version: 1, name: '现场资料' }),
encoding: 'utf-8',
createParents: true,
success: () => console.log('配置文件已保存'),
fail: (err) => console.error('保存配置失败:', err.errCode, err.errMsg)
})
}
// 读取文本后可直接显示或交给 JSON.parse 继续处理。
function loadConfig() {
readTextFile({
storageId,
path: '/reports/config.json',
encoding: 'utf-8',
success: (result) => {
console.log('配置内容:', result.text)
},
fail: (err) => console.error('读取配置失败:', err.errCode, err.errMsg)
})
}
// 小型二进制文件可以读入 ArrayBuffer;大文件请使用 copyFromStorage。
function loadSmallBinaryFile() {
readFile({
storageId,
path: '/reports/thumbnail.jpg',
maxBytes: 5 * 1024 * 1024,
success: (result) => {
console.log('二进制文件大小:', result.size, '字节')
// result.data 是 ArrayBuffer,可交给业务自己的图片或文件处理逻辑。
},
fail: (err) => console.error('读取二进制文件失败:', err.errCode, err.errMsg)
})
}
// 写入小型二进制内容时传入 ArrayBuffer;真实项目中可替换为业务生成的数据。
function saveSmallBinaryFile() {
const data = new Uint8Array([0x31, 0x32, 0x33]).buffer
writeFile({
storageId,
path: '/reports/sample.bin',
data,
createParents: true,
success: () => console.log('二进制文件已保存'),
fail: (err) => console.error('保存二进制文件失败:', err.errCode, err.errMsg)
})
}
模块四:双向复制、进度与取消
copyToStorage 和 copyFromStorage 用于单文件复制;copyBatchToStorage 和 copyBatchFromStorage 用于一次处理多个文件。任务开始会通过 onStart 返回 taskId,进度通过 onProgress 返回;用户点击取消时,单文件调用 cancelTask,批量任务调用 cancelBatchTask。
下面的示例包含“导出到 U 盘”“从 U 盘导入”和“点击按钮取消”三种常见操作。复制前请确保源文件已经存在,并为每次任务保存 taskId。
应用本地路径可以使用真实绝对路径,也可以使用 uni.env.USER_DATA_PATH、uni.env.CACHE_PATH 等返回的沙盒路径;存储侧路径仍使用授权目录内的相对路径。
import {
copyToStorage,
copyFromStorage,
cancelTask
} from '@/uni_modules/lizhao-usb-storage'
const storageId = '请替换为已取得的 storageId'
let currentTaskId = ''
// 请替换为应用中真实存在的本地文件路径:
// uni-app 可使用 plus.io.convertLocalFileSystemURL('_doc/backup.zip');
// uni-app x 可使用 `${uni.env.USER_DATA_PATH}/backup.zip`。
const localBackupPath = '请替换为应用可读取的 backup.zip 路径'
// 把应用内生成的压缩包导出到 U 盘。
function exportBackup() {
copyToStorage({
sourcePath: localBackupPath,
storageId,
targetPath: '/backup/backup.zip',
createParents: true,
onStart: (result) => {
// 复制登记成功后才能取消任务。
currentTaskId = result.taskId
},
onProgress: (progress) => {
const percent = progress.totalBytes > 0
? Math.floor(progress.completedBytes * 100 / progress.totalBytes)
: 0
console.log(`正在导出:${percent}%(${progress.state})`)
},
success: () => {
currentTaskId = ''
console.log('备份已导出到 U 盘')
},
fail: (err) => {
currentTaskId = ''
console.error('导出失败:', err.errCode, err.errMsg)
}
})
}
// 把 U 盘中的资料复制到应用自己的本地目录。
function importReport() {
copyFromStorage({
storageId,
sourcePath: '/reports/today.pdf',
// uni-app / uni-app x 请替换成各自应用目录中的绝对路径。
targetPath: '请替换为应用目录中的 today.pdf 绝对路径',
onStart: (result) => { currentTaskId = result.taskId },
onProgress: (progress) => console.log('正在导入:', progress.completedBytes, '/', progress.totalBytes),
success: () => {
currentTaskId = ''
console.log('资料已导入应用')
},
fail: (err) => {
currentTaskId = ''
console.error('导入失败:', err.errCode, err.errMsg)
}
})
}
// 绑定到“取消”按钮;没有正在执行的任务时不执行任何操作。
function cancelCurrentCopy() {
if (!currentTaskId) {
console.log('当前没有正在执行的复制任务')
return
}
cancelTask({
taskId: currentTaskId,
success: () => console.log('已请求取消复制'),
fail: (err) => console.error('取消失败:', err.errCode, err.errMsg)
})
}
模块五:整目录扫描与导入导出
需要整批处理一个文件夹时,直接传入源目录和目标目录即可保留层级与空目录。源目录的内容会直接放入目标目录,例如源内的 图片/a.jpg 会复制成目标内的 图片/a.jpg,不会再追加源文件夹名称。
先查看外置目录中的文件数量和大小;onEntries 每批返回条目,可边接收边更新列表,success 只返回统计。提供可选 targetPath 时,还会检查应用目标目录内的同名条目,此检查不创建文件。
import { scanDirectory } from '@/uni_modules/lizhao-usb-storage'
scanDirectory({
source: 'storage',
storageId,
path: '/lizhao-demo',
targetPath: uni.env.USER_DATA_PATH + '/usb-import',
batchSize: 100,
onEntries: (batch) => console.log('本批目录条目', batch.entries),
success: (res) => {
console.log('文件数', res.fileCount, '子目录数', res.directoryCount)
console.log('总字节', res.totalBytes, '同名条目', res.conflictItems)
},
fail: (err) => console.error('扫描失败', err.errCode, err.errMsg)
})
扫描应用本地目录时使用 source: 'local' 和本地绝对 path;只扫描本地目录无需 storageId。如同时提供外置存储相对 targetPath 做同名检查,则需传入 storageId。文件大小未知时,条目的 size=-1、sizeKnown=false,汇总的 totalBytes=-1;可继续显示 knownBytes 与 unknownSizeFiles。目录数量与文件数量分开统计,不包含源目录本身。
把外置目录导入应用时,目标使用应用可写的本地绝对目录;目标不存在会在扫描成功后创建。
import { copyDirectoryFromStorage } from '@/uni_modules/lizhao-usb-storage'
copyDirectoryFromStorage({
storageId,
sourcePath: '/lizhao-demo',
targetPath: uni.env.USER_DATA_PATH + '/usb-import',
conflictStrategy: 'skip',
onProgress: (res) => console.log(res.phase, res.completedItems, res.totalItems),
success: (res) => console.log('导入结果', res.results),
fail: (err) => console.error('导入失败或取消', err.errCode, err.data)
})
把应用目录导出到外置存储时,源使用本地绝对目录,目标使用授权范围内的相对目录。
import { copyDirectoryToStorage } from '@/uni_modules/lizhao-usb-storage'
copyDirectoryToStorage({
storageId,
sourcePath: uni.env.USER_DATA_PATH + '/usb-import',
targetPath: '/usb-backup',
conflictStrategy: 'rename',
success: (res) => console.log('完成', res.completedItems, '失败', res.failedItems),
fail: (err) => console.error('导出失败或取消', err.errCode, err.data)
})
目录任务先扫描、后复制;phase 为 scanning 或 copying。超过条目或深度限制时返回 9036002,扫描阶段不会写入目标。每次任务最多 10000 个条目,maxEntries 默认 10000;maxDepth 默认 32、上限 64,根目录的直接子项为深度 1;扫描 batchSize 默认 100、上限 256。三个参数均须为正整数。
已有同类目录会合并。同名文件按 skip、fail 或 rename 处理;fail 记录该项失败后继续,文件与文件夹类型冲突会记失败,受影响子目录中的条目也会逐项记录。success 表示目录任务处理结束,请同时检查 failedItems 和 results。
扫描和目录复制均可在 onStart 保存任务编号,再请求取消;取消不删除已完成的文件,只清理本次未完成的新文件。
import { scanDirectory, cancelDirectoryTask } from '@/uni_modules/lizhao-usb-storage'
let directoryTaskId = ''
scanDirectory({
source: 'storage', storageId, path: '/',
onStart: (res) => { directoryTaskId = res.taskId },
fail: (err) => console.log('任务结束', err.errCode, err.data),
complete: () => { directoryTaskId = '' }
})
// 将此函数绑定到页面的取消按钮。
function cancelDirectory() {
if (directoryTaskId != '') cancelDirectoryTask({ taskId: directoryTaskId })
}
取消请求成功后,原任务通过 fail 返回 9036014。复制中取消或发生整体错误时,err.data.partialResult 保留已处理结果与 unprocessedItems;单项失败则在正常结果中列明。扫描、单文件、批量和目录复制的任务编号不可混用。同一 storageId 的任务结束前,请等待结果后再发起其他文件操作。
模块六:连接状态与资源释放
listStorages 可恢复仍有效的目录授权;Android 还可用 listMountedVolumes 查看系统识别到的存储设备。onStorageEvent 用于接收插件能够确认的授权和连接变化,页面销毁或不再使用插件时调用 releaseResources。
页面打开时可以同时读取已保存授权和当前识别到的存储设备;页面关闭时移除监听并释放插件资源,避免重复监听或保留已经结束的任务。
import {
getCapabilities,
listStorages,
listMountedVolumes,
Event,
offStorageEvent,
releaseResources
} from '@/uni_modules/lizhao-usb-storage'
import type { StorageEvent } from '@/uni_modules/lizhao-usb-storage'
// 取消监听时需传入注册时的同一个函数。
const storageListener = (event: StorageEvent) => {
console.log('存储状态变化:', event.type, event.storageId || event.volumeId || '')
// 收到拔出或权限变化通知后,建议重新调用 listStorages 检查授权是否仍有效。
}
function startStorageMonitor() {
Event(storageListener)
// 查询仍然有效的目录授权。
listStorages({
success: (storages) => console.log('可用目录授权数量:', storages.length),
fail: (err) => console.error('查询目录授权失败:', err.errCode, err.errMsg)
})
// 只有能力表明确支持时才查询全部已识别设备;iOS 请以目录授权和文件操作结果为准。
getCapabilities({
success: (capabilities) => {
if (!capabilities.mountedVolumeDiagnostics) return
listMountedVolumes({
success: (volumes) => console.log('当前识别到的存储设备:', volumes.length),
fail: (err) => console.error('查询存储设备失败:', err.errCode, err.errMsg)
})
}
})
}
// 页面销毁、退出功能页或不再使用插件时调用。
function stopStorageMonitor() {
offStorageEvent(storageListener)
releaseResources({
success: () => console.log('USB 存储资源已释放'),
fail: (err) => console.error('释放资源失败:', err.errCode, err.errMsg)
})
}
参数说明(常用配置)
| 参数 | 类型 | 说明 |
|---|---|---|
storageId |
string |
pickStorageRoot 或 listStorages 返回的目录标识。 |
path |
string |
所选目录内的文件或文件夹路径;空字符串或 / 表示所选目录。 |
recursive |
boolean |
默认 false。创建多层文件夹或删除非空文件夹时传 true;listEntries 只列出当前层,不支持传 true。 |
createParents |
boolean |
父目录不存在时是否按路径创建,默认 false。 |
overwrite |
boolean |
是否请求覆盖同名文件,默认 false。向外置存储写入时请保留默认值;同名文件会报错,原文件不会被覆盖。 |
maxBytes |
number |
readFile 允许读取的最大字节数;超过限制返回读取失败,建议使用复制 API。 |
encoding |
string |
文本编码,首版建议使用 utf-8。 |
items |
BatchCopyItem[] |
批量复制的文件清单;每项包含源路径和目标路径。 |
conflictStrategy |
'skip' / 'fail' / 'rename' |
同名文件处理方式,默认 skip,不覆盖已有文件。 |
totalBytes |
number |
批量清单项的可选文件大小;所有清单项都传入后可显示总字节进度,否则批量进度中的总字节数为 -1。 |
maxEntries / maxDepth |
number |
目录扫描或复制的条目与深度限制,默认 10000 / 32,上限 10000 / 64。 |
batchSize |
number |
目录扫描每次 onEntries 的条目数,默认 100,上限 256。 |
文件路径必须位于用户已选择并授权的目录内。不要传入完整系统地址、盘符路径、包含 .. 的越界路径或空文件名;违规返回 9036017。
API 用途速查
| 类别 | API | 用途 | 关键返回 |
|---|---|---|---|
| 能力与授权 | getCapabilities |
查询当前平台是否支持插件能力 | UsbStorageCapabilities |
| 能力与授权 | pickStorageRoot |
让使用者选择允许访问的目录 | StorageHandle |
| 能力与授权 | listStorages |
恢复已保存且仍有效的目录 | StorageHandle[] |
| 能力与授权 | listMountedVolumes |
Android 查看系统识别到的存储设备及状态;iOS 返回不支持 | MountedVolume[] |
| 能力与授权 | checkAllFilesAccessPermission |
查询 Android“所有文件访问”设置状态 | { granted } |
| 能力与授权 | openAllFilesAccessSettings |
打开 Android“所有文件访问”设置页 | { ok: true } |
| 文件与目录 | listEntries |
列出当前目录内容 | StorageEntry[] |
| 文件与目录 | statEntry |
查询文件或目录信息 | StorageEntry |
| 文件与目录 | readFile |
读取小型二进制文件 | { data: ArrayBuffer, size } |
| 文件与目录 | readTextFile |
按编码读取文本 | { text, size } |
| 文件与目录 | writeFile |
写入 ArrayBuffer |
{ ok: true } |
| 文件与目录 | writeTextFile |
写入文本 | { ok: true } |
| 文件与目录 | createDirectory |
创建目录 | StorageEntry |
| 文件与目录 | deleteEntry |
删除文件或目录;根目录禁止删除 | { ok: true } |
| 文件与目录 | renameEntry |
在同一父目录内重命名 | StorageEntry |
| 文件复制 | copyToStorage |
把单个应用文件复制到外置存储 | TaskResult |
| 文件复制 | copyFromStorage |
把单个外置存储文件复制到应用目录 | TaskResult |
| 文件复制 | copyBatchToStorage |
把多份应用文件复制到外置存储 | BatchCopyResult |
| 文件复制 | copyBatchFromStorage |
把多份外置存储文件复制到应用目录 | BatchCopyResult |
| 文件复制 | cancelTask |
取消单文件复制任务 | { ok: true } |
| 文件复制 | cancelBatchTask |
取消整批复制任务 | { ok: true } |
| 整目录处理 | scanDirectory |
分批扫描、统计与同名预检 | DirectoryScanResult |
| 整目录处理 | copyDirectoryToStorage |
应用整目录导出,保留层级和空目录 | DirectoryCopyResult |
| 整目录处理 | copyDirectoryFromStorage |
外置存储整目录导入应用 | DirectoryCopyResult |
| 整目录处理 | cancelDirectoryTask |
请求取消扫描或目录复制 | { ok: true } |
| 状态与资源 | onStorageEvent |
开始接收存储连接变化 | 持续回调 StorageEvent |
| 状态与资源 | offStorageEvent |
停止接收指定回调 | 无返回值 |
| 状态与资源 | releaseStorage |
释放指定目录并清除保存的授权 | { ok: true } |
| 状态与资源 | releaseResources |
等待当前任务结束并停止监听;已保存的目录授权可在下次继续恢复 | { ok: true } |
二进制 readFile 面向小文件并受 maxBytes 限制。大文件请使用 copyFromStorage,避免把完整内容一次性放入 JavaScript 内存。
进阶说明:文件复制、进度与取消
复制开始后先触发 onStart,立即提供可取消的 taskId;随后通过 onProgress 报告进度,最终由 success 或 fail 结束。
let taskId = ''
copyToStorage({
// 请替换为应用目录中的真实文件绝对路径。
sourcePath: '请替换为应用目录中的 export.zip 绝对路径',
storageId,
targetPath: '/backup/export.zip',
createParents: true,
onStart: (res) => {
taskId = res.taskId
},
onProgress: (progress) => {
console.log(progress.taskId, progress.completedBytes, progress.totalBytes, progress.state)
},
success: () => console.log('复制完成'),
fail: (err) => console.error('复制失败', err.errCode, err.errMsg)
})
// 用户点击取消按钮时调用
if (taskId) {
cancelTask({ taskId })
}
copyFromStorage 用于从外置存储复制到应用可写的本地路径。每个 storageId 同时只允许一个活动写入任务;取消时会停止复制,并尝试清理本次新建且未完成的目标文件。对已完成、未知或已取消的 taskId 请求取消时,会返回明确错误。
批量导入导出
调用方把多份文件整理为 items 清单,插件会按顺序处理;单项失败不会中断后续文件,最终结果会列出每个文件的完成、跳过或失败状态。处理整个文件夹时可直接使用模块五的目录复制接口,无需自行展开清单。
import {
copyBatchToStorage,
cancelBatchTask
} from '@/uni_modules/lizhao-usb-storage'
const storageId = '请替换为已取得的 storageId'
let batchTaskId = ''
// 一次把多份应用文件导出到 U 盘;totalBytes 已知时可显示总字节进度。
copyBatchToStorage({
storageId,
items: [
{
sourcePath: '请替换为应用中的 report.pdf 绝对路径',
targetPath: '/backup/report.pdf',
totalBytes: 1024 * 1024
},
{
sourcePath: '请替换为应用中的 photo.jpg 绝对路径',
targetPath: '/backup/photo.jpg'
}
],
// 默认 skip:发现同名文件时保留 U 盘原文件并跳过当前项。
conflictStrategy: 'skip',
createParents: true,
onStart: (result) => {
// 批量 taskId 专门用于 cancelBatchTask,不要与单文件 taskId 混用。
batchTaskId = result.taskId
},
onProgress: (progress) => {
console.log(
`第 ${progress.currentIndex}/${progress.totalItems} 项,` +
`完成 ${progress.completedItems},跳过 ${progress.skippedItems},失败 ${progress.failedItems}`
)
},
success: (result) => {
batchTaskId = ''
console.log('批量导出完成,逐项结果:', result.results)
},
fail: (error) => {
batchTaskId = ''
console.error('批量任务未完成:', error.errCode, error.errMsg)
}
})
// 绑定到“取消批量导出”按钮;取消后已完成文件不会回滚。
function cancelBatchExport() {
if (!batchTaskId) return
cancelBatchTask({
taskId: batchTaskId,
success: () => console.log('已请求取消批量导出'),
fail: (error) => console.error('取消批量任务失败:', error.errCode, error.errMsg)
})
}
copyBatchFromStorage 的 sourcePath 使用外置存储相对路径,targetPath 使用应用本地绝对路径。conflictStrategy 支持 skip、fail、rename;未填写时默认使用 skip,填写其他值会返回参数错误。其中 rename 会自动生成带序号的新文件名,最多尝试 1000 个可用名称,始终不会覆盖已有文件;候选名称全部被占用时,该文件会记录为失败。批量任务的单项失败会记录在 results 中并继续处理其他文件;只有参数错误、平台不支持或主动取消整个任务时才调用批量 fail。
同一个 storageId 同时只能运行一个批量任务。任务结束前,请等待进度回调,不要同时浏览或修改同一授权目录;此时发起的其他文件操作会返回 9036013。cancelBatchTask 的成功回调表示取消请求已经收到,当前文件会停止且不会再处理下一项,原批量调用随后会通过 fail 返回 9036014。如果系统正在确认当前文件是否存在,取消结果会在本次确认结束后返回。
批量进度常用字段:
| 字段 | 说明 |
|---|---|
currentIndex / totalItems |
当前处理到第几个文件,以及清单总数。 |
completedItems / skippedItems / failedItems |
已完成、已跳过和已失败的文件数量。 |
currentSourcePath / currentTargetPath |
当前文件的源路径和实际目标路径;自动改名时目标路径会随之变化。 |
completedBytes / totalBytes |
已成功复制的字节数;清单未完整提供文件大小时 totalBytes 为 -1。 |
state |
running 为处理中,completed 为整批处理结束,cancelled 为已取消。 |
BatchCopyResult 会返回上述数量汇总、completedBytes、totalBytes 和 results。results 中每一项都包含源路径、最终目标路径、完成状态和实际复制字节数;失败或跳过时还会包含错误码与原因。
覆盖策略
向外置存储写入或复制文件时,建议使用默认的 overwrite: false。发现同名文件会返回错误并保留原文件,可更换目标文件名,或在批量任务中使用 rename 自动改名。Android 不支持直接覆盖外置存储中的已有文件;iOS 只有在系统允许安全替换时才会处理 overwrite: true,失败时仍保留原文件。
能力、授权与连接状态
getCapabilities仅查询平台能力,不会自动申请权限。pickStorageRoot打开系统文件选择器,取得并保存用户选择的目录授权。checkAllFilesAccessPermission与openAllFilesAccessSettings仅对应 Android“所有文件访问”设置;iOS 没有这项设置。使用本插件读写已授权目录不需要开启它。UsbStorageCapabilities.usbPermission不能作为文件可读写的判断依据,文件访问以所选目录的授权结果为准。onStorageEvent/offStorageEvent用于接收或停止接收插件能够确认的存储变化。Android 可以接收系统插拔变化;iOS 会在授权、释放或文件访问确认断开时通知,不能监听所有未授权设备的即时插拔。releaseStorage撤销指定目录的授权;releaseResources会等待当前任务结束、停止接收通知并清理当前使用的资源,但不会删除已保存的目录授权。
返回值说明
| 返回类型 | 主要内容 | 使用建议 |
|---|---|---|
UsbStorageCapabilities |
是否支持、当前平台、连接状态查询和事件能力 | 页面进入时先查询,用于决定是否显示相关入口。 |
StorageHandle |
storageId、目录名称、是否可读写 |
保存 storageId,后续文件操作继续使用。 |
StorageEntry |
路径、名称、文件或目录类型、大小、修改时间 | 用于渲染文件列表和判断下一步操作。 |
MountedVolume |
存储设备名称、是否可移除、挂载状态、只读状态 | 仅 Android 提供,用于显示连接情况,不能代替目录选择结果。 |
StorageTaskStart |
可取消的 taskId |
在 onStart 中保存,用户取消时传给取消接口。 |
StorageProgress |
已完成字节数、总字节数、任务状态 | 用于显示单文件复制进度。 |
BatchCopyProgress |
当前文件、文件数量、成功/跳过/失败数量和总进度 | 用于显示整批任务的实时状态。 |
BatchCopyResult |
整批汇总以及每个文件的实际处理结果 | 完成后逐项展示结果或记录失败原因。 |
DirectoryScanResult |
文件/子目录数、已知字节、未知大小文件数和同名数 | 不包含源根;任一文件大小未知时总字节为 -1。 |
DirectoryCopyResult |
文件和目录的逐项结果及完成、跳过、失败、未处理数 | 取消时查看 fail.data.partialResult,正常结束也要检查失败数量。 |
UsbStorageFail |
errCode、errMsg,部分错误带补充信息 |
在 fail 回调中根据错误码给出可操作提示。 |
支持平台
| 运行环境 | 支持情况 | 行为 |
|---|---|---|
| uni-app Android App | 支持 | Android 5.0 及以上;使用前在系统文件选择器中选择可访问目录。 |
| uni-app x Android App | 支持 | 文件操作与 uni-app 相同;系统版本还需满足项目要求。 |
| uni-app iOS App | 支持 | iOS 13 及以上;通过系统“文件”选择 U 盘、移动硬盘或其他可用目录。 |
| uni-app x iOS App | 支持 | 文件操作与 uni-app 相同;首次使用需要选择允许访问的目录。 |
| HarmonyOS App | 不支持 | 返回 9036001,不返回伪造的空成功结果。 |
| Web / 微信、支付宝等小程序 | 不支持 | 返回 9036001。 |
插件只能访问系统文件选择器中显示且已获授权的目录。能否识别 U 盘或移动硬盘取决于设备系统、磁盘格式和供电情况;部分设备只能选择盘内文件夹。插件不提供磁盘格式化或分区功能。
Android 本地目录递归扫描及整目录导出需 Android 8.0 / API26 及以上;外置目录扫描、整目录导入和原有单文件/批量能力仍支持原系统范围。iOS 目录任务支持 iOS 13 及以上。目录任务不跟随符号链接,也不允许源目标重叠。
错误码说明
| 错误码 | 含义 | 建议 |
|---|---|---|
9036001 |
当前平台不支持此项存储能力 | 检查平台表;iOS 调用 Android 专有的设备枚举或设置入口时也会返回此错误。 |
9036002 |
参数不合法 | 检查必填字段、编码和回调。 |
9036003 |
原生运行环境不可用 | 等待 App 页面和运行环境就绪后重试。 |
9036004 |
系统文件选择器不可用或目录无法访问 | 更换系统文件选择器中的目录。 |
9036005 |
用户取消目录选择 | 重新提示用户选择。 |
9036006 |
目录授权未保存或已失效 | 重新选择目录。 |
9036007 |
存储目录标识不存在 | 使用最新 storageId。 |
9036008 |
存储尚未准备好 | 先选择目录或恢复已保存的授权。 |
9036009 |
文件或目录不存在 | 检查路径及介质连接状态。 |
9036010 |
文件/目录类型不匹配 | 确认 API 与条目类型一致。 |
9036011 |
读取失败 | 检查权限、大小限制和介质状态。 |
9036012 |
写入失败 | 检查只读状态、父目录和同名文件。 |
9036013 |
复制失败 | 检查源/目标路径和可用空间。 |
9036014 |
操作已取消 | 根据业务决定是否重新复制。 |
9036015 |
介质只读或没有写权限 | 更换可写目录或介质。 |
9036016 |
设备已断开或介质已卸载 | 重新连接后重新授权。 |
9036017 |
路径越界、根目录保护或操作不安全 | 使用授权树内的相对路径。 |
9036018 |
其他系统错误 | 保留 data 脱敏信息并记录系统版本。 |
注意事项
- 首次接入或升级插件后,请重新制作并安装对应平台的自定义基座,或重新打包 App。
- 移动硬盘可能需要独立供电。设备已连接但文件选择器看不到目录时,先检查 OTG 转接、供电和系统文件管理器。
- 即使设备已经连接,首次读写前仍需在系统文件选择器中选择允许访问的文件夹。
- 存储设备断开或目录权限变化后,原目录可能无法访问;请重新连接并选择目录。
常见问题
插入 U 盘后,系统文件选择器里没有显示怎么办?
先用手机自带文件管理器确认能否看到 U 盘,再检查 OTG 转接、磁盘供电和磁盘格式。移动硬盘供电不足时,可能连接后仍无法正常显示。
已经连接设备,为什么仍然不能读写?
连接设备只表示系统检测到了存储介质。首次使用仍需调用 pickStorageRoot,让使用者选择允许访问的目录。
usbPermission 为 false,是否代表不能读写文件?
不是。文件能否读写以 pickStorageRoot 返回的目录授权为准,建议直接完成目录选择后再执行文件操作。
批量导出遇到同名文件怎么办?
默认会跳过同名文件并保留原文件。可把 conflictStrategy 设为 rename 自动生成新名称;设为 fail 时会把冲突文件记录为失败,并继续处理后续文件。
拔出 U 盘后原来的 storageId 还能继续使用吗?
不能保证。重新连接后先调用 listStorages 检查授权是否仍有效;无有效结果时,请重新选择目录。
iOS、HarmonyOS、Web 或小程序能否使用?
Android 和 iOS App 支持主要文件操作。iOS 需要在系统“文件”界面选择目录,不能查询全部已挂载设备或打开 Android 专有设置;HarmonyOS、Web 和小程序会通过失败回调明确返回不支持。
数据处理说明
插件仅在设备本地处理使用者选择的文件和目录,不上传文件,不连接作者服务器,也不包含广告。应用仍需根据自身业务说明文件用途,并妥善处理通过插件读取或导出的数据。
许可证
本插件遵循仓库许可证。
联系方式
信-微:l-z-1-8-7-1512-5421(-去掉,不这样写会被和谐)
作者系列 UTS 插件
以下为已在 DCloud 插件市场上架的作者系列 UTS 插件,可按业务场景组合使用。未列出的插件表示当前未确认公开市场页,后续上架后再补充。
| 插件 | 能力方向 | 插件市场 |
|---|---|---|
lizhao-nfc-pro |
NFC 标签读写、NDEF、IsoDep 与诊断 | 查看插件 |
lizhao-float-window |
悬浮窗、画中画、权限与诊断 | 查看插件 |
lizhao-device-id |
设备标识、隐私策略与诊断 | 查看插件 |
lizhao-scan-pro |
原生扫码、连续扫码、相册识别 | 查看插件 |
lizhao-choose-file |
原生文件选择、上传、进度与取消 | 查看插件 |
lizhao-bg-audio |
背景音频播放、队列、倍速与事件 | 查看插件 |
lizhao-smart-tts |
系统 TTS、云端合成、听书方案 | 查看插件 |
lizhao-share-plus |
系统分享、远程文件下载后分享 | 查看插件 |
lizhao-sqlite-pro |
原生 SQLite、迁移、备份与诊断 | 查看插件 |
lizhao-icon-pro |
SVG 图标组件、多主题与缓存 | 查看插件 |
lizhao-cast-screen |
DLNA 投屏、AirPlay 路由入口 | 查看插件 |
lizhao-call-kit |
电话、短信、通讯录原生能力 | 查看插件 |
lizhao-app-keepalive |
应用保活、唤醒、自愈与报告 | 查看插件 |
lizhao-doc-corrector |
文档扫描、矫正、增强与识别 | 查看插件 |
lizhao-emu-detect |
模拟器环境检测、风险评分与证据 | 查看插件 |
lizhao-gallery-pro |
相册媒体分页、筛选、缩略图与导出 | 查看插件 |
lizhao-video-thumb |
视频封面、批量取帧与 Base64 返回 | 查看插件 |
lizhao-ble |
BLE 扫描、连接、读写、通知与自动重连 | 查看插件 |
lizhao-sse-pro |
SSE、Line、JSONL 与 Raw 流式请求 | 查看插件 |
lizhao-pdf-pro |
PDF 阅读、签批、真实写回与页面处理 | 查看插件 |
lizhao-serial-port |
路径串口、USB 串口、多会话收发与诊断 | 查看插件 |
lizhao-wechat-kit |
微信登录、分享、支付、小程序与客服 | 查看插件 |
lizhao-video-editor |
视频裁剪、压缩、取帧与 FFmpeg/FFprobe | 查看插件 |
lizhao-vpn-pro |
企业 VPN、IKEv2、安全接入与脱敏诊断 | 查看插件 |
lizhao-camera-pro |
原生相机、拍照录像、水印与媒体保存 | 查看插件 |
lizhao-tcp-pro |
TCP 客户端、服务端、多连接与诊断 | 查看插件 |
lizhao-notify-pro |
本地通知、点击动作、进度与定时提醒 | 查看插件 |

收藏人数:
购买源码授权版(
试用
赞赏(0)
下载 6486
赞赏 5
下载 12748787
赞赏 1959
赞赏
京公网安备:11010802035340号