MAUI Blazor跨端OCR扫描器相机无法启动求助及可行代码需求
MAUI Blazor 跨平台OCR扫描器完整实现
一、平台权限配置
Android(Platform/Android/AndroidManifest.xml)
添加相机相关权限:
<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" android:required="true" /> <uses-feature android:name="android.hardware.camera.autofocus" />
iOS(Platform/iOS/Info.plist)
添加相机权限说明:
<key>NSCameraUsageDescription</key> <string>需要访问相机进行OCR文字识别</string>
二、Blazor 组件代码(Pages/OcrScanner.razor)
@page "/ocrscanner" @inject IJSRuntime JSRuntime @implements IAsyncDisposable <h3>OCR扫描器</h3> <div class="camera-container"> <video id="videoFeed" width="320" height="240" style="transform: scaleX(-1);" /> <canvas id="currentFrame" class="d-none" width="320" height="240" /> </div> <div class="mt-3"> <button class="btn btn-primary" @onclick="StartCamera">启动相机</button> <button class="btn btn-secondary ms-2" @onclick="StopCamera">停止相机</button> <button class="btn btn-success ms-2" @onclick="CaptureAndRecognize">识别文字</button> </div> <div class="mt-3"> <h4>识别结果:</h4> <pre>@ocrResult</pre> </div> @code { private string ocrResult = ""; private IJSObjectReference? _jsModule; private DotNetObjectReference<OcrScanner>? _dotNetReference; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { _jsModule = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./js/ocr-handler.js"); _dotNetReference = DotNetObjectReference.Create(this); } } private async Task StartCamera() { await _jsModule!.InvokeVoidAsync("startCamera", "videoFeed"); } private async Task StopCamera() { await _jsModule!.InvokeVoidAsync("stopCamera", "videoFeed"); } private async Task CaptureAndRecognize() { await _jsModule!.InvokeVoidAsync("captureFrameForOcr", "videoFeed", "currentFrame", _dotNetReference); } [JSInvokable] public async Task UpdateOcrResult(string recognizedText) { ocrResult = recognizedText; StateHasChanged(); } public async ValueTask DisposeAsync() { if (_jsModule != null) { await _jsModule.DisposeAsync(); } _dotNetReference?.Dispose(); } }
三、JavaScript 处理代码(wwwroot/js/ocr-handler.js)
先在wwwroot/index.html的<head>中引入Tesseract.js(前端OCR库):
<script src="https://cdn.jsdelivr.net/npm/tesseract.js@5.0.2/dist/tesseract.min.js"></script>
然后编写ocr-handler.js:
import * as Tesseract from 'tesseract.js'; // 初始化OCR工作线程,支持中英双语 const ocrWorker = Tesseract.createWorker({ logger: m => console.log(m) // 可选,打印识别日志 }); async function initWorker() { await ocrWorker.load(); await ocrWorker.loadLanguage('eng+chi_sim'); await ocrWorker.initialize('eng+chi_sim'); } // 启动相机 export async function startCamera(videoId) { try { const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment', // 指定使用后置摄像头 width: { ideal: 1280 }, height: { ideal: 720 } } }); const videoElement = document.getElementById(videoId); videoElement.srcObject = stream; await videoElement.play(); } catch (error) { console.error('相机启动失败:', error); alert('无法访问相机,请检查应用权限设置'); } } // 停止相机 export function stopCamera(videoId) { const videoElement = document.getElementById(videoId); if (videoElement?.srcObject) { const stream = videoElement.srcObject; const tracks = stream.getTracks(); tracks.forEach(track => track.stop()); videoElement.srcObject = null; } } // 捕获帧并执行OCR export async function captureFrameForOcr(videoId, canvasId, dotNetHelper) { // 确保OCR线程已初始化 if (!ocrWorker.isLoaded()) { await initWorker(); } const videoElement = document.getElementById(videoId); const canvasElement = document.getElementById(canvasId); const ctx = canvasElement.getContext('2d'); // 将视频帧绘制到画布 ctx.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height); const imageData = ctx.getImageData(0, 0, canvasElement.width, canvasElement.height); // 执行OCR识别 const recognitionResult = await ocrWorker.recognize(imageData); // 将结果传回C#端 await dotNetHelper.invokeMethodAsync('UpdateOcrResult', recognitionResult.data.text); } // 页面卸载时销毁OCR线程 window.addEventListener('beforeunload', async () => { await ocrWorker.terminate(); });
四、核心注意事项
- 权限优先级:必须先配置Android和iOS的平台权限,否则相机请求会被系统拒绝
- 后置摄像头:通过
facingMode: 'environment'强制使用后置摄像头,更适合文档扫描场景 - 资源清理:实现
IAsyncDisposable清理JS模块和DotNet引用,避免内存泄漏 - 错误处理:添加相机启动失败的捕获逻辑,方便调试权限或设备兼容问题
内容的提问来源于stack exchange,提问作者neha
相关产品推荐
相关产品推荐

