跳转到内容

能力 · API

WebUSB API

发布于

有限可用不支持的浏览器: Firefox (Desktop)、Firefox (Android)、Safari (macOS)、Safari (iOS)WICG 草案

WebUSB 把没有被操作系统类驱动接管的 USB 设备(开发板、烧录器、自制仪器)交给页面:navigator.usb.requestDevice() 弹出选择器,返回的 USBDevice 依次打开、选配置、声明接口,然后执行控制、批量、中断或同步传输。规范由 WHATWG 发布,WICG 地址会重定向过去。

Chrome 61 与 Edge 79 在桌面端和 Android 上实现了该 API,Samsung Internet 镜像 Chrome(BCD api.USB)。Firefox 没有实现,Mozilla 对 WebUSB 的标准立场为反对(mozilla/standards-positions#100);Safari 没有实现;Android WebView 暴露 navigator.usb 但所有调用都会失败(crbug.com/41441927)。Chrome 70 起在专用 worker 中可用,Chrome 118 起在扩展的 service worker 中可用(BCD api.USB.worker_support)。

navigator.usb.requestDevice(options)
navigator.usb.getDevices()
device.open()
device.selectConfiguration(configurationValue)
device.claimInterface(interfaceNumber)
device.selectAlternateInterface(interfaceNumber, alternateSetting)
device.controlTransferIn(setup, length)
device.controlTransferOut(setup, data)
device.transferIn(endpointNumber, length)
device.transferOut(endpointNumber, data)
device.isochronousTransferIn(endpointNumber, packetLengths)
device.isochronousTransferOut(endpointNumber, data, packetLengths)
device.clearHalt(direction, endpointNumber)
device.releaseInterface(interfaceNumber)
device.reset()
device.close()
device.forget()

每个方法都返回 Promise。requestDevice() 以一个 USBDevice 兑现,且必须在瞬时用户激活内调用;getDevices() 以本源已获授权且仍连着的设备兑现。输入传输以 USBInTransferResult(data 为 DataView,status 为 "ok"、"stall" 或 "babble")兑现;输出传输以 USBOutTransferResult(bytesWritten、status)兑现。navigator.usb 会为已授权设备触发 connect 与 disconnect 事件。所有接口都是 [SecureContext]。

requestDevice(options) 接受 USBDeviceRequestOptions 字典,其 filters 成员必填(空数组表示列出全部设备)。控制传输接受 USBControlTransferParameters 字典。

字典 成员 类型 必填 说明
USBDeviceRequestOptions filters sequence<USBDeviceFilter> 是 设备只要满足任一过滤器中所有已给出的成员即匹配。
USBDeviceRequestOptions exclusionFilters sequence<USBDeviceFilter> 否 匹配其中任一项的设备不出现在选择器中(Chrome 117)。
USBDeviceFilter vendorId unsigned short 否 USB 厂商 ID。
USBDeviceFilter productId unsigned short 否 产品 ID;只有与 vendorId 同时给出才有效。
USBDeviceFilter classCode octet 否 设备或接口类。
USBDeviceFilter subclassCode octet 否 需要 classCode。
USBDeviceFilter protocolCode octet 否 需要 subclassCode。
USBDeviceFilter serialNumber DOMString 否 精确匹配的序列号字符串。
USBControlTransferParameters requestType USBRequestType 是 "standard"、"class" 或 "vendor"。
USBControlTransferParameters recipient USBRecipient 是 "device"、"interface"、"endpoint" 或 "other"。
USBControlTransferParameters request octet 是 setup 包的 bRequest。
USBControlTransferParameters value unsigned short 是 wValue。
USBControlTransferParameters index unsigned short 是 wIndex;接收方为 "interface" 或 "endpoint" 时,低字节必须指向已声明的接口或其端点。

USBDevice 以只读方式暴露描述符树:configurations(USBConfiguration 数组),每项含 interfaces(USBInterface),每个接口含 alternates(USBAlternateInterface),其中列出 endpoints(USBEndpoint,有 endpointNumber、direction、type、packetSize)。vendorId、productId、manufacturerName、productName、serialNumber 与 opened 是属性。

方法 异常 条件
requestDevice() TypeError 过滤器或排除过滤器无效:有 productId 没 vendorId、有 subclassCode 没 classCode、有 protocolCode 没 subclassCode。
requestDevice() SecurityError 没有瞬时激活,或文档被禁用了 usb Permissions Policy 特性。
requestDevice() NotFoundError 用户关闭了选择器,或没有已连接设备匹配过滤器。
任一 USBDevice 方法 NotFoundError 设备已不再连接。
selectConfiguration()、claimInterface()、selectAlternateInterface()、clearHalt()、各传输 NotFoundError 配置值、接口号、备用设置或端点不存在。
selectConfiguration()、claimInterface()、各传输 InvalidStateError 设备未打开、未选择配置,或(传输与 selectAlternateInterface())接口未声明。
claimInterface() SecurityError 接口属于受保护类(例如 HID 或大容量存储)且上下文不是无限制的。
transferIn()、transferOut() InvalidAccessError 端点不是 "bulk" 或 "interrupt"。
isochronousTransferIn()、isochronousTransferOut() InvalidAccessError 端点不是 "isochronous"。
open()、selectConfiguration()、claimInterface()、releaseInterface()、selectAlternateInterface()、clearHalt()、reset()、各传输 NetworkError 平台操作或传输因 stall、babble 之外的原因失败。
进行中的传输 AbortError 被 close()、releaseInterface() 或 selectAlternateInterface() 中止。

规范还维护一份黑名单(按 idVendor、idProduct、bcdDevice 匹配),其中的设备直接不出现在选择器里,而不是以异常拒绝。Chromium 另外会在传输超时时给出 TimeoutError,在同步传输的包长度与缓冲区不符时给出 DataError,在端点号大于 15 时给出 IndexSizeError。

每个示例都先检查 navigator.usb,并给出没有它时的分支。只有 requestDevice() 需要点击,之后的传输可以在任何后续代码中运行。

按厂商 ID 配对设备,带复用路径与回退

Section titled “按厂商 ID 配对设备,带复用路径与回退”

getDevices() 返回此前已授权的设备,所以选择器只在首次访问出现。没有该 API 时,函数返回可供界面展示的原因,例如指向桌面浏览器或原生工具的链接。

async function pair(vendorId) {
if (!navigator.usb) {
return { device: null, reason: '当前浏览器不提供 WebUSB。' };
}
const granted = await navigator.usb.getDevices();
let device = granted.find((d) => d.vendorId === vendorId);
if (!device) {
try {
device = await navigator.usb.requestDevice({ filters: [{ vendorId }] });
} catch (err) {
if (err.name === 'NotFoundError') return { device: null, reason: '未选择设备。' };
throw err;
}
}
await device.open();
if (device.configuration === null) await device.selectConfiguration(1);
return { device, reason: null };
}

多数操作系统在 open() 之后已经设好 device.configuration;显式的 selectConfiguration(1) 覆盖它为 null 的情况。

找到第一个厂商自定义接口(interfaceClass 为 0xFF),声明它,定位批量端点,交换一个数据包。此处的 SecurityError 表示接口属于受保护类,WebUSB 不会交出;NetworkError 表示操作系统驱动仍占着它。

async function roundTrip(device, payload) {
const iface = device.configuration.interfaces.find((i) =>
i.alternates.some((a) => a.interfaceClass === 0xff),
);
if (!iface) throw new Error('该设备没有厂商自定义接口。');
await device.claimInterface(iface.interfaceNumber);
const alt = iface.alternates[0];
const outEp = alt.endpoints.find((e) => e.direction === 'out' && e.type === 'bulk');
const inEp = alt.endpoints.find((e) => e.direction === 'in' && e.type === 'bulk');
const sent = await device.transferOut(outEp.endpointNumber, payload);
if (sent.status === 'stall') await device.clearHalt('out', outEp.endpointNumber);
const reply = await device.transferIn(inEp.endpointNumber, inEp.packetSize);
await device.releaseInterface(iface.interfaceNumber);
return reply.status === 'ok' ? new Uint8Array(reply.data.buffer) : null;
}

"stall" 状态不是异常:设备暂停了端点,重试前按文档调用 clearHalt() 即可恢复。

已授权设备通过 navigator.usb 的 disconnect 事件通告移除。把 event.device 与正在使用的设备比较,避免无关硬件重置界面。

function watch(device, onLost) {
if (!navigator.usb) return () => {};
const handler = (event) => {
if (event.device === device) onLost();
};
navigator.usb.addEventListener('disconnect', handler);
return () => navigator.usb.removeEventListener('disconnect', handler);
}

断开时仍在进行的传输会以 NotFoundError 拒绝,所以 onLost 也是丢弃未决 Promise 队列的地方。

规范

规范状态
WebUSB APIWICG 草案
WebUSB API: requestDevice() methodWHATWG 现行标准
WebUSB API: claimInterface() methodWHATWG 现行标准
WebUSB API: blocklistWHATWG 现行标准
  • 图例
  • 支持
  • 部分支持
  • 需开启标志
  • 不支持
  • 未知
浏览器 / 平台支持版本置信度来源备注
Chrome (Desktop)支持61高来源—
Chrome (Android)支持61高来源1
Edge (Desktop)支持79高来源2
Firefox (Desktop)不支持—高来源3
Firefox (Android)不支持—高来源45
Safari (macOS)不支持—高来源6
Safari (iOS)不支持—高来源78
Samsung Internet支持8.0高来源9
WebView (Android)不支持—高来源10
Opera支持48高来源—
  1. 由 browser-compat-data 镜像自 Chrome 的数据推导。
  2. 由 browser-compat-data 镜像自 Chrome 的数据推导。
  3. browser-compat-data 未记录 Firefox 的支持。
  4. browser-compat-data 未记录 Firefox for Android 的支持。
  5. 由 browser-compat-data 镜像自 Firefox 的数据推导。
  6. browser-compat-data 未记录 Safari 的支持。
  7. browser-compat-data 未记录 iOS 版 Safari 的支持。
  8. 由 browser-compat-data 镜像自 Safari 的数据推导。
  9. 由 browser-compat-data 镜像自 Chrome Android 的数据推导。
  10. browser-compat-data 未记录 WebView Android 的支持。

源数据: /compatibility/web-usb.json · 全球使用占比: 74 % (StatCounter 2026-05)

来源: 规范 · MDN · 最近核验 2026-10-03 · 置信度: 高 (由来源计算)