更新记录

1.0.0(2026-09-21)

  • 首次发布
  • 支持设备枚举、USB 授权、存储初始化、目录浏览、文本/二进制(Base64)读写、本地与U盘双向复制、创建目录、删除、插拔监听、所有文件访问权限检查等

平台兼容性

uni-app(4.72)

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

uni-app x(4.72)

Chrome Safari Android Android插件版本 iOS 鸿蒙 微信小程序
× × 5.0 1.0.0 × × ×

zy-usb-drive

U 盘(USB 大容量存储设备)读写 UTS 插件,移植自 uni-app 原生插件 usbDriveModule,基于 libaumsme.jahnen.libaums:core:0.10.0)直接访问 USB Host。

特性

  • USB 存储设备枚举(U 盘 / 读卡器 / 移动硬盘)
  • USB 授权自动申请,授权成功后自动续接初始化(无需二次点击)
  • U 盘存储初始化:总容量 / 已用空间 / 剩余空间 / 簇大小 / 卷标
  • 目录浏览(自动过滤 System Volume InformationLOST.DIR
  • 文本文件读写(UTF-8)
  • 二进制文件读写(Base64,NO_WRAP
  • 本地文件 ↔ U 盘文件双向复制(流式分块传输,支持大文件)
  • 创建多级目录、删除文件/目录
  • USB 插拔事件监听(插入 / 拔出)
  • Android 11+ 「所有文件访问权限」检查并自动跳转系统授权页

平台支持

平台 支持
Android
iOS
H5
小程序

最低 Android SDK 版本: 21 (Android 5.0) 设备须支持 USB Host(OTG)。libaums 自带 armeabi-v7a / arm64-v8a 原生库。

依赖

utssdk/app-android/config.json 中已固定依赖版本:

{
    "abis": ["armeabi-v7a", "arm64-v8a"],
    "minSdkVersion": 21,
    "dependencies": ["me.jahnen.libaums:core:0.10.0"]
}

快速开始

import {
    checkAllFilesAccessPermission,
    getUsbDriveDeviceList,
    initUsbStorage,
    registerUsbStateReceiver,
    getUsbFileList,
    readTextFile,
    writeTextFile,
    releaseResources
} from '@/uni_modules/zy-usb-drive'

标准流程

// 1. (Android 11+ 推荐)确认「所有文件访问权限」,无权限会自动跳转系统设置页
checkAllFilesAccessPermission({
    success: (res) => {
        console.log('是否有所有文件访问权限:', res.hasPermission)
    }
})

// 2. 监听 U 盘插拔(onEvent 可多次回调)
registerUsbStateReceiver({
    onEvent: (e) => {
        if (e.event === 'deviceAttached') console.log('U 盘已插入')
        if (e.event === 'deviceDetached') console.log('U 盘已拔出')
    },
    success: (res) => console.log(res.errMsg)
})

// 3. 枚举设备
getUsbDriveDeviceList({
    success: (res) => {
        console.log('设备列表:', res.devices)
        // 4. 初始化第一个设备(无权限时会自动弹系统授权框)
        initUsbStorage({
            deviceIndex: res.devices[0].index,
            statCapacity: true,   // 需要准确的剩余/已用空间时开启;默认 false 不统计
            success: (info) => {
                console.log('容量:', info.capacity, '剩余:', info.freeSpace)
            },
            fail: (err) => console.error(err.errCode, err.errMsg)
        })
    }
})

// 5. 离开页面释放资源
// releaseResources()

API

所有 API 均支持 success / fail / complete 回调,失败回调参数为 UniError(含 errCodeerrMsg)。

checkAllFilesAccessPermission(options?)

检查 Android 11+ 的「所有文件访问权限」,无权限时自动跳转系统授权页。

type UsbDrivePermissionResult = {
    hasPermission: boolean       // 是否已拥有权限
    isAndroidROrHigher: boolean  // 是否运行在 Android 11 及以上
    jumpedToSettings: boolean    // 是否已自动跳转到系统授权页
}

getUsbDriveDeviceList(options?)

枚举 USB 存储设备。

type UsbDriveDeviceInfo = {
    deviceId: number
    deviceName: string                  // 如 /dev/bus/usb/001/002
    productName: string | null
    manufacturerName: string | null
    vendorId: number
    productId: number
    index: number                       // 供 initUsbStorage 使用
}
// success 回调: { devices: UsbDriveDeviceInfo[] }

initUsbStorage(options)

初始化指定设备(挂载其第一个分区)。

参数 类型 必填 默认值 说明
deviceIndex number - 设备在列表中的 index
statCapacity boolean false 是否统计剩余/已用空间,见下方说明
type UsbDriveStorageInfo = {
    capacity: number       // 总容量(字节),总是准确
    occupiedSpace: number  // 已用空间(字节,含文件系统元数据占用);capacityStat 为 false 时为 0
    freeSpace: number      // 剩余空间(字节);capacityStat 为 false 时为 0
    chunkSize: number      // 簇大小(字节),总是准确
    volumeLabel: string
    capacityStat: boolean  // 是否已统计剩余/已用空间
}

statCapacity:是否统计容量信息(默认 false

统计剩余/已用空间需要读取 FSInfo,必要时还要扫描 FAT 表,大容量 U 盘会有一定耗时, 因此默认不统计

取值 行为
false 不做任何额外 IO,初始化更快;occupiedSpace / freeSpace 恒为 0,capacityStat 为 false。文件浏览与读写不受影响
true 读取 FSInfo 的空闲簇计数;若该计数无效(见下)则自动扫描 FAT 表统计,返回准确数值,capacityStat 为 true
// 需要展示准确容量时
initUsbStorage({
    deviceIndex: 0,
    statCapacity: true,
    success: (info) => {
        if (info.capacityStat) {
            console.log(`已用 ${info.occupiedSpace} / 剩余 ${info.freeSpace}`)
        }
    }
})

关于容量数值的可靠性

occupiedSpace = capacity - freeSpace(与 libaums 语义一致),因此「已用空间」包含保留扇区、 FAT 表与根目录区占用,会略大于文件大小之和,这是正常的。

部分 U 盘的 FSInfo 未写入空闲簇计数(保持 0xFFFFFFFF),而 libaums 按有符号 32 位解析成 -1 且未做兜底,会导致 freeSpace = -1 × 簇大小(负数)、occupiedSpace 超过 capacitystatCapacity: true 时插件在检测到该异常会自行扫描 FAT 表统计空闲簇,保证返回的 freeSpace 不为负; 仅当扫描也失败时才按 freeSpace = 0 上报(日志中会有 FSInfo 空闲簇计数无效 提示)。

未授权时内部会自动发起 USB 授权弹窗,用户允许后自动继续初始化并回调 success; 拒绝或等待超过 60 秒会走 fail9030004 / 9030016)。

registerUsbStateReceiver(options?)

注册 U 盘插拔监听。注册结果走 success/fail,插拔事件走 onEvent(可多次)。

type UsbDriveEvent = {
    event: string      // 'deviceAttached' | 'deviceDetached'
    deviceId: number
    deviceName: string
}

getUsbFileList(options?)

获取指定目录下的文件列表。

参数 类型 必填 说明
path string 目录路径,缺省为根目录 /
type UsbDriveFileInfo = {
    name: string
    path: string
    absolutePath: string
    isDirectory: boolean
    size: number        // 字节,目录恒为 0
}
// success 回调: { files: UsbDriveFileInfo[] }

readTextFile(options)

读取文本文件(UTF-8)。filePath 必填;success 回调 { content: string }

readBinaryFile(options)

读取二进制文件。filePath 必填;success 回调 { base64: string }

writeTextFile(options)

写入文本文件(UTF-8)。filePathcontent 必填。文件不存在时创建,存在时覆盖。

父目录必须已存在,可先用 createUsbDirectory 创建。

writeBinaryFile(options)

写入二进制文件。filePathbase64Data 必填。文件不存在时创建,存在时覆盖。

copyLocalFileToUsb(options)

把本地文件复制到 U 盘。localFilePathusbFilePath 必填。

copyUsbFileToLocal(options)

把 U 盘文件复制到本地,本地父目录不存在时自动创建。usbFilePathlocalFilePath 必填。

复制进度 onProgress

copyLocalFileToUsbcopyUsbFileToLocal 支持 onProgress 回调,在复制过程中 按整数百分比变化多次触发0 ~ 100),可用于驱动进度条。

type UsbDriveCopyProgress = {
    loaded: number    // 已传输字节数
    total: number     // 待传输总字节数
    progress: number  // 进度百分比(0 ~ 100 的整数)
}
copyLocalFileToUsb({
    localFilePath: '/sdcard/Download/video.mp4',
    usbFilePath: '/video.mp4',
    : (p) => {
        console.log(`${p.progress}%  ${p.loaded}/${p.total}`)
    },
    success: (res) => console.log('复制完成', res.errMsg),
    fail: (err) => console.error(err.errCode, err.errMsg)
})

createUsbDirectory(options)

创建 U 盘目录,支持多级(如 /a/b/c),已存在的层级自动跳过。dirPath 必填。

deleteUsbFile(options)

删除 U 盘上的文件或目录。filePath 必填;不允许删除根目录 /

releaseResources(options?)

释放资源:注销广播接收器、关闭已打开的 USB 设备、清空文件系统引用与待处理回调。 建议在页面 onUnload 中调用。

错误码

错误码 说明
9030001 参数错误
9030002 USB 存储未初始化,请先调用 initUsbStorage
9030003 未检测到 USB 存储设备
9030004 USB 权限被拒绝
9030005 初始化 USB 存储失败
9030006 文件或目录不存在
9030007 获取文件列表失败
9030008 读取文件失败
9030009 写入文件失败
9030010 删除失败
9030011 创建目录失败
9030012 复制文件失败
9030013 打开权限设置页失败
9030014 应用上下文获取失败
9030015 释放资源失败
9030016 USB 权限授权超时
9030017 指定路径是目录,不能按文件读取

权限与要求

Android 权限已在 utssdk/app-android/AndroidManifest.xml 中声明:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.usb.host" android:required="true" />

MANAGE_EXTERNAL_STORAGE(所有文件访问权限)无法通过运行时弹窗申请, 需由用户前往系统设置手动开启,插件提供 checkAllFilesAccessPermission 自动跳转。

使用须知

  1. 必须先调用 getUsbDriveDeviceListinitUsbStoragedeviceIndex 指向设备列表下标; 若中间发生插拔导致缓存失效,插件会自动重新枚举。
  2. 写入文件的父目录必须已存在,插件不会自动创建中间目录(与原生实现保持一致)。
  3. 覆盖写文件会自动截断,避免新内容较短时残留旧数据。
  4. 读写操作在子线程串行执行,大文件复制不会阻塞 UI。
  5. 拔出 U 盘后 currentFs 会被清空,需重新执行 getUsbDriveDeviceList + initUsbStorage

参考

  • libaums: https://github.com/magnusja/libaums
  • Android USB Host 文档: https://developer.android.com/guide/topics/connectivity/usb/host

隐私、权限声明

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

android.permission.READ_EXTERNAL_STORAGE android.permission.WRITE_EXTERNAL_STORAGE android.permission.MANAGE_EXTERNAL_STORAGE android.hardware.usb.host

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

U盘内文件与设备信息仅在本机读写,不会上传至任何服务器

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

暂无用户评论。