如何通过WebUSB API向配对的存储类USB设备写入文件?
能否通过Web USB API向USB存储设备写入文件?
结论:技术上可行,但需要自行实现大量底层协议逻辑,没有现成的高层封装
在Chrome >=61的环境下,通过navigator.usb拿到USBDevice并打开后,确实可以发起USB传输向U盘、SD卡(通过USB读卡器连接)这类存储设备写入数据,但Web USB API仅提供底层的USB端点传输能力,不提供文件系统级别的操作——你需要自己实现从USB存储协议到文件系统的完整逻辑。
核心要求的协议与步骤
针对USB闪存盘、USB读卡器连接的SD卡这类USB Mass Storage类设备,需要实现以下核心内容:
- USB Mass Storage Bulk-Only Transport (BOT)协议:这是USB存储设备与主机通信的标准传输协议,用于封装SCSI命令。
- SCSI块设备命令集:存储设备遵循SCSI规范,你需要发送对应的SCSI命令完成基础操作:
INQUIRY:识别设备类型与参数READ CAPACITY:获取设备的块大小、总块数等信息WRITE10/WRITE16:向指定块地址写入数据块
- 文件系统逻辑:完成底层块读写后,还需自行实现目标文件系统(如FAT32、NTFS)的管理,包括解析/修改文件分配表、管理目录项、处理文件的创建与写入等操作。
大致代码流程示例
以下是拿到usbDevice后的关键操作片段(仅展示底层传输,文件系统逻辑需自行补充):
// 1. 声明存储设备对应的接口(通常是接口0) await usbDevice.claimInterface(0); // 2. 获取设备的bulk输入/输出端点(需根据设备描述符查询实际编号) const bulkOutEndpoint = usbDevice.configuration.interfaces[0].alternate.endpoints.find(e => e.direction === 'out'); const bulkInEndpoint = usbDevice.configuration.interfaces[0].alternate.endpoints.find(e => e.direction === 'in'); // 3. 构造并发送SCSI WRITE10命令(以写入单个数据块为例) const lbaAddress = 0x00000000; // 要写入的起始块地址 const blockCount = 1; // 写入的块数 const writeCommand = new Uint8Array([ 0x2A, 0x00, (lbaAddress >> 24) & 0xFF, (lbaAddress >> 16) & 0xFF, (lbaAddress >> 8) & 0xFF, lbaAddress & 0xFF, 0x00, 0x00, (blockCount >> 8) & 0xFF, blockCount & 0xFF, 0x00 ]); // 发送命令包 await usbDevice.transferOut(bulkOutEndpoint.endpointNumber, writeCommand); // 4. 发送要写入的文件数据(需转换为与设备块大小匹配的Uint8Array) const fileData = new TextEncoder().encode('Hello, USB Storage!'); // 假设设备块大小为512字节,需填充到块大小 const paddedData = new Uint8Array(512); paddedData.set(fileData); await usbDevice.transferOut(bulkOutEndpoint.endpointNumber, paddedData); // 5. 读取设备的状态响应 const statusResponse = await usbDevice.transferIn(bulkInEndpoint.endpointNumber, 3); // 解析响应判断写入是否成功
注意事项
- 不同存储设备的端点编号、块大小可能不同,需要先通过设备描述符查询这些参数
- 错误处理非常关键,需要处理传输失败、设备响应错误等情况
- 实现完整的文件系统逻辑复杂度很高,若没有特殊需求,优先使用浏览器原生的文件操作API(如
FileReader、FileSystemAccess API)
内容的提问来源于stack exchange,提问作者Bryce
相关产品推荐
相关产品推荐

