如何在Angular应用中连接扫描设备并实现浏览器端扫描预览?
Angular浏览器扫描设备连接与扫描件预览(开源方案)
核心方案选型
要实现浏览器直接连接扫描设备并预览扫描件,优先选择浏览器原生Web API配合轻量开源工具,无需付费依赖,主要有两个可行方向:
方案一:MediaDevices API(兼容性优先)
大多数带扫描功能的设备(如一体机、带扫描的摄像头)可被识别为视频输入设备,通过MediaDevices API获取设备流,捕获帧后转成图片显示在浏览器。
1. Angular组件实现代码
import { Component, ElementRef, ViewChild } from '@angular/core'; @Component({ selector: 'app-scan-page', template: ` <div class="scan-container"> <h3>扫描设备连接提示</h3> <button (click)="startScan()" *ngIf="!isScanning">启动扫描</button> <button (click)="stopScan()" *ngIf="isScanning">停止扫描</button> <button (click)="captureScan()" *ngIf="isScanning">捕获扫描件</button> <video #scanVideo autoplay playsinline class="scan-video" *ngIf="isScanning"></video> <div class="scan-result" *ngIf="capturedImage"> <h4>扫描预览</h4> <img [src]="capturedImage" alt="扫描文档" class="result-image"> </div> </div> `, styles: [` .scan-container { max-width: 800px; margin: 20px auto; } .scan-video { width: 100%; border: 1px solid #eee; margin: 15px 0; } .result-image { max-width: 100%; border-radius: 4px; } button { margin-right: 10px; padding: 8px 16px; cursor: pointer; } `] }) export class ScanPageComponent { @ViewChild('scanVideo') scanVideo!: ElementRef<HTMLVideoElement>; isScanning = false; capturedImage: string | null = null; mediaStream: MediaStream | null = null; async startScan() { try { // 获取扫描设备ID(筛选名称含"Scanner"/"扫描"的视频输入设备) const scanDeviceId = await this.getScanDeviceId(); if (!scanDeviceId) { alert('未检测到可用扫描设备,请检查连接'); return; } // 请求设备媒体流 this.mediaStream = await navigator.mediaDevices.getUserMedia({ video: { deviceId: { exact: scanDeviceId }, width: { ideal: 1920 }, height: { ideal: 1080 } } }); // 绑定流到video元素 this.scanVideo.nativeElement.srcObject = this.mediaStream; this.isScanning = true; } catch (err) { console.error('扫描设备连接失败:', err); alert('无法连接扫描设备,请授权浏览器访问权限并检查设备状态'); } } async getScanDeviceId(): Promise<string | undefined> { const devices = await navigator.mediaDevices.enumerateDevices(); return devices.find(device => device.kind === 'videoinput' && (device.label.includes('Scanner') || device.label.includes('扫描')) )?.deviceId; } captureScan() { if (!this.scanVideo.nativeElement) return; const canvas = document.createElement('canvas'); canvas.width = this.scanVideo.nativeElement.videoWidth; canvas.height = this.scanVideo.nativeElement.videoHeight; const ctx = canvas.getContext('2d'); ctx?.drawImage(this.scanVideo.nativeElement, 0, 0); // 转成base64图片直接显示 this.capturedImage = canvas.toDataURL('image/png'); } stopScan() { this.mediaStream?.getTracks().forEach(track => track.stop()); this.mediaStream = null; this.isScanning = false; } }
2. 关键注意事项
- 必须在HTTPS环境下使用(本地开发
localhost可例外) - 首次访问会触发浏览器权限请求,需用户授权设备访问
- 部分老旧扫描仪可能无法被识别为视频输入设备,此时可切换到WebUSB方案
方案二:WebUSB API(针对独立USB扫描仪)
对于不支持MediaDevices的独立USB扫描仪,可通过WebUSB API直接与设备通信,前提是设备支持USB WebAPI规范。
1. 核心实现逻辑
// 需替换为你的扫描仪Vendor ID和对应通信协议 async connectUSBScan() { try { // 请求用户选择目标USB设备 const device = await navigator.usb.requestDevice({ filters: [{ vendorId: 0xXXXX }] // 替换为扫描仪的Vendor ID }); await device.open(); await device.selectConfiguration(1); await device.claimInterface(0); // 发送扫描指令(需参考扫描仪的USB协议文档) await device.controlTransferOut({ requestType: 'class', recipient: 'interface', request: 0x06, value: 0x0100, index: 0x00 }); // 读取扫描返回数据并转成图片 const transferResult = await device.transferIn(0x81, 65535); const blob = new Blob([transferResult.data], { type: 'image/png' }); this.capturedImage = URL.createObjectURL(blob); } catch (err) { console.error('USB扫描设备连接失败:', err); alert('无法连接USB扫描仪,请检查设备驱动及协议兼容性'); } }
2. 注意事项
- 需要提前获取扫描仪的Vendor ID/Product ID以及官方USB通信协议文档
- 仅支持Chrome、Edge等现代浏览器,兼容性有限
- 需用户手动选择设备,无法自动检测
开源库辅助方案
如果不想手写底层逻辑,可使用轻量开源库简化开发:
simple-scan:基于MediaDevices API封装,提供扫描、图像裁剪、亮度调整等功能,遵循MIT协议- 集成方式:安装后在Angular组件中调用,核心逻辑与方案一一致,库已封装设备筛选和图像处理细节
需求满足验证
- 浏览器直接显示:通过将扫描帧转成base64或Blob URL,直接绑定到
<img>元素,无需保存到本地设备 - 开源免费:所有方案基于浏览器原生API或开源协议库,无付费依赖
内容的提问来源于stack exchange,提问作者Bharanitharan Sakthivel
相关产品推荐
相关产品推荐

