ZXing的BrowserMultiformatReader在React中解码失败原因排查
问题:ZXing无法解码上传的条码图片
组件可正常上传条码图片,但解码失败,报错:
Uncaught (in promise) NotFoundException2: No MultiFormat Readers were able to detect the code.
需求为上传任意格式的条码图片,在页面展示图片并返回条码文本内容,但ZXing无法识别目标条码格式。
原代码
import React, { useRef, useState } from 'react'; import { BrowserMultiFormatReader, BarcodeFormat, DecodeHintType, Result } from '@zxing/library'; import './BarcodeScanner.css'; // Importamos los estilos CSS const BarcodeScanner: React.FC = () => { const fileInputRef = useRef<HTMLInputElement>(null); const [capturedImage, setCapturedImage] = useState<string | null>(null); const [barcodeResult, setBarcodeResult] = useState<string | null>(null); const captureImageFromFile = (file: File) => { try { const reader = new FileReader(); reader.onload = async () => { console.log(reader.result); if (typeof reader.result === 'string') { setCapturedImage(reader.result); decodeBarcode(reader.result); } }; reader.readAsDataURL(file); } catch (error) { setBarcodeResult("Error al decodificar"); console.error('Error al decodificar el código de barras:', error); } }; const decodeBarcode = async (imageSrc: string) => { try { const img = new Image(); img.onload = async () => { const formats = [BarcodeFormat.EAN_13, BarcodeFormat.EAN_8]; const hints = new Map(); hints.set(DecodeHintType.POSSIBLE_FORMATS, formats); const codeReader = new BrowserMultiFormatReader(hints); codeReader.timeBetweenDecodingAttempts = 300; const result = await codeReader.decodeFromImage(img as HTMLImageElement); setBarcodeResult(result.getText()); } img.src = imageSrc; } catch (error) { setBarcodeResult("Error al decodificar"); console.error('Error al decodificar el código de barras:', error); } }; const handleFileInputChange = (event: React.ChangeEvent<HTMLInputElement>) => { const file = event.target.files && event.target.files[0]; if (file) { captureImageFromFile(file); } }; return ( <div> <h1>Captura de Imagen desde Archivo</h1> <div style={{ marginTop: '20px', textAlign: 'center' }}> <input type="file" accept="image/*" onChange={handleFileInputChange} ref={fileInputRef} style={{ display: 'none' }} /> <button onClick={() => fileInputRef.current?.click()}>Seleccionar Archivo</button> </div> {capturedImage && ( <div style={{ marginTop: '20px', textAlign: 'center' }}> <h2>Captura Realizada</h2> <img src={capturedImage} alt="Captura" style={{ maxWidth: '320px' }} /> </div> )} {barcodeResult && ( <div style={{ marginTop: '20px', textAlign: 'center' }}> <h2>Resultado del Código de Barras</h2> <p>{barcodeResult}</p> </div> )} </div> ); }; export default BarcodeScanner;
解决方案
1. 解除条码格式限制
原代码仅指定了EAN_13和EAN_8两种格式,若上传的条码不属于这两类,ZXing会直接识别失败。有两种处理方式:
- 添加更多常用格式:把需要支持的条码类型加入
formats数组(如QR_CODE、CODE_128等) - 自动检测所有格式:移除
POSSIBLE_FORMATS的设置,让ZXing自动尝试所有支持的条码类型
示例(自动检测所有格式):
const decodeBarcode = async (imageSrc: string) => { try { // ... 其他代码 const hints = new Map(); // 移除这行格式限制代码:hints.set(DecodeHintType.POSSIBLE_FORMATS, formats); const codeReader = new BrowserMultiFormatReader(hints); // ... 其他代码 } catch (error) { // ... 错误处理 } };
2. 修复图片加载的异步处理问题
原代码中img.onload的异步函数错误无法被外层try/catch捕获,改用Promise封装图片加载逻辑,确保错误能被正确处理:
const decodeBarcode = async (imageSrc: string) => { try { // 用Promise等待图片加载完成 const img = await new Promise<HTMLImageElement>((resolve, reject) => { const imgElement = new Image(); imgElement.onload = () => resolve(imgElement); imgElement.onerror = () => reject(new Error('图片加载失败')); imgElement.src = imageSrc; }); // ... 解码逻辑 } catch (error) { setBarcodeResult("解码失败"); console.error('条码解码错误:', error); } };
3. 优化解码兼容性
- 若图片尺寸过大,可通过canvas压缩后再解码
- 提示用户上传清晰、无反光、条码完整的图片
修改后的完整代码
import React, { useRef, useState } from 'react'; import { BrowserMultiFormatReader } from '@zxing/library'; import './BarcodeScanner.css'; const BarcodeScanner: React.FC = () => { const fileInputRef = useRef<HTMLInputElement>(null); const [capturedImage, setCapturedImage] = useState<string | null>(null); const [barcodeResult, setBarcodeResult] = useState<string | null>(null); const captureImageFromFile = (file: File) => { try { const reader = new FileReader(); reader.onload = () => { if (typeof reader.result === 'string') { setCapturedImage(reader.result); decodeBarcode(reader.result); } }; reader.readAsDataURL(file); } catch (error) { setBarcodeResult("解码失败"); console.error('条码解码错误:', error); } }; const decodeBarcode = async (imageSrc: string) => { try { const img = await new Promise<HTMLImageElement>((resolve, reject) => { const imgElement = new Image(); imgElement.onload = () => resolve(imgElement); imgElement.onerror = () => reject(new Error('图片加载失败')); imgElement.src = imageSrc; }); // 不限制格式,自动检测所有支持的条码类型 const codeReader = new BrowserMultiFormatReader(); const result = await codeReader.decodeFromImage(img); setBarcodeResult(result.getText()); } catch (error) { setBarcodeResult("解码失败"); console.error('条码解码错误:', error); } }; const handleFileInputChange = (event: React.ChangeEvent<HTMLInputElement>) => { const file = event.target.files && event.target.files[0]; if (file) { captureImageFromFile(file); } }; return ( <div> <h1>图片条码识别</h1> <div style={{ marginTop: '20px', textAlign: 'center' }}> <input type="file" accept="image/*" onChange={handleFileInputChange} ref={fileInputRef} style={{ display: 'none' }} /> <button onClick={() => fileInputRef.current?.click()}>选择图片文件</button> </div> {capturedImage && ( <div style={{ marginTop: '20px', textAlign: 'center' }}> <h2>上传的图片</h2> <img src={capturedImage} alt="上传的条码图片" style={{ maxWidth: '320px' }} /> </div> )} {barcodeResult && ( <div style={{ marginTop: '20px', textAlign: 'center' }}> <h2>条码内容</h2> <p>{barcodeResult}</p> </div> )} </div> ); }; export default BarcodeScanner;
内容的提问来源于stack exchange,提问作者Juan Carlos GIRARDINI
相关产品推荐
相关产品推荐

