更新记录

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 会打开“文件”选择界面。插件只操作使用者明确选择的目录。

下载与导入

  1. 打开 插件市场页面,选择“使用 HBuilderX 导入插件”,导入到 uni-app 或 uni-app x 项目。
  2. 保留完整的 uni_modules/lizhao-usb-storage 目录,不要修改插件目录名。
  3. 在页面脚本中从插件根目录导入:
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 脱敏信息并记录系统版本。

注意事项

  1. 首次接入或升级插件后,请重新制作并安装对应平台的自定义基座,或重新打包 App。
  2. 移动硬盘可能需要独立供电。设备已连接但文件选择器看不到目录时,先检查 OTG 转接、供电和系统文件管理器。
  3. 即使设备已经连接,首次读写前仍需在系统文件选择器中选择允许访问的文件夹。
  4. 存储设备断开或目录权限变化后,原目录可能无法访问;请重新连接并选择目录。

常见问题

插入 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 本地通知、点击动作、进度与定时提醒 查看插件

隐私、权限声明

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

Android 和 iOS:需要用户通过系统文件选择器授予所选目录的读取和写入权限,并在设备本地保存该目录授权。插件本身不声明额外系统权限,不申请读取或写入全部存储的权限,也不申请 USB 设备通信权限;Android 常规文件操作无需开启“所有文件访问权限”,仅在应用主动调用相关接口时查询或打开该设置,由使用者决定是否授权。HarmonyOS、Web 和小程序暂不支持,不申请相关权限。

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

按应用调用处理用户已授权目录中的文件内容、文件名、路径、大小、修改时间及存储卷状态,用于文件浏览、读写、复制和连接状态提示;在应用本地保存目录授权信息,用于下次恢复访问。上述处理均在设备本地完成,文件按调用参数保存到指定位置;插件不上传数据,不向任何服务器发送数据,无数据接收服务器地址。

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

无广告,不包含广告 SDK,不展示广告。