如何构建适用于 Android 和 iOS 的 React Native MRZ 护照扫描仪
手动验证旅行证件容易出错且效率低下。护照、身份证和其他符合国际民航组织 (ICAO) 标准的旅行证件上印有的机读区 (MRZ) 以结构化的机器可读格式编码所有关键身份信息——但在移动设备上提取这些信息需要精准的 OCR 引擎和实时摄像头数据处理流程。Dynamsoft MRZ Scanner SDK将这两项功能封装在一个 React Native 调用中,可在 Android 和 iOS 系统上
如何构建适用于 Android 和 iOS 的 React Native MRZ 护照扫描仪
手动验证旅行证件容易出错且效率低下。护照、身份证和其他符合国际民航组织 (ICAO) 标准的旅行证件上印有的机读区 (MRZ) 以结构化的机器可读格式编码所有关键身份信息——但在移动设备上提取这些信息需要精准的 OCR 引擎和实时摄像头数据处理流程。Dynamsoft MRZ Scanner SDK将这两项功能封装在一个 React Native 调用中,可在 Android 和 iOS 系统上运行。
你将构建:一个 React Native 应用(Android API 21+ / iOS 13+),该应用打开一个实时摄像头扫描仪,读取任何 TD1/TD2/TD3 旅行证件的 MRZ,并使用 v3.2.5002 显示解析后的字段(姓名、证件号码、国籍、日期)dynamsoft-mrz-scanner-bundle-react-native。
演示视频:React Native MRZ 扫描器实战
要点总结
- 本教程展示了如何通过一次 SDK 调用,无需原生模块样板代码,将实时 MRZ 扫描集成到 React Native 应用程序中。
dynamsoft-mrz-scanner-bundle-react-native公开了一个MRZScanner.launch()API,可以一次性处理相机权限、取景器 UI 和 OCR。- SDK 返回一个完全解析的结果对象( ) ,
result.data其中包含命名字段—— 、、、、firstName等等——从而无需手动解析 MRZ 行。lastNamedocumentNumbernationality - 这种模式可直接应用于机场值机亭、酒店登记系统以及任何需要快速、无需人工干预的文件验证工作流程。
开发者常见问题
- 如何在不使用原生 Android/iOS 代码的情况下,使用 React Native 扫描护照 MRZ?
- 为什么
MRZScanner.launch()在授予权限后,Android 系统仍然会出现相机权限错误? - Dynamsoft MRZ Scanner 可以离线使用吗?还是每次扫描都需要网络连接?
先决条件
开始之前,请确保您已准备好以下物品:
- Node.js ≥ 18
- React Native 0.79(采用新架构)
- 最新版Android Studio,连接运行 Android 5.0 (API 21) 或更高版本的设备或模拟器。
- 适用于 iOS 13.0 及更高版本的Xcode (最新版,仅限 macOS)
- JDK 17
- Dynamsoft MRZ Scanner 许可证密钥——该项目附带一个有时限的试用密钥,需要网络连接。
访问dynamsoft.com/customer/license/trialLicense获取 30 天免费试用许可证
步骤 1:安装和配置 SDK
使用 npm 安装项目依赖项。Dynamsoft 的两个软件包——MRZ 扫描仪软件包和底层 Capture Vision 引擎——已在文件中声明,package.json并将自动安装。
<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code>npm <span style="color:#336666">install</span>
</code></span></span></span>
相关 SDK 依赖项位于package.json:
<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code><span style="color:#9999ff">"dependencies"</span>: {
<span style="color:#9999ff">"dynamsoft-capture-vision-react-native"</span>: <span style="color:#cc3300">"3.2.5002"</span>,
<span style="color:#9999ff">"dynamsoft-mrz-scanner-bundle-react-native"</span>: <span style="color:#cc3300">"3.2.5002"</span>,
<span style="color:#9999ff">"react"</span>: <span style="color:#cc3300">"19.0.0"</span>,
<span style="color:#9999ff">"react-native"</span>: <span style="color:#cc3300">"0.79.0"</span>
}
</code></span></span></span>
对于 iOS,请在安装 CocoaPods 依赖项之后进行安装npm install:
<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code><span style="color:#336666">cd </span>ios <span style="color:#555555">&&</span> pod <span style="color:#336666">install</span> <span style="color:#555555">&&</span> <span style="color:#336666">cd</span> ..
</code></span></span></span>
步骤 2:配置许可证并启动 MRZ 扫描器
从捆绑包中导入 `<module>` MRZScanner、 `<module> MRZScanConfig` 和 ` <module>`,然后使用您的许可证密钥调用 `<module>`。SDK 将接管摄像头,显示其内置的扫描用户界面,并且仅在用户完成扫描或取消扫描后返回。EnumResultStatusMRZScanner.launch(config)
<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code><span style="color:#006699"><strong>import</strong></span> {
EnumResultStatus,
MRZScanConfig,
MRZScanner,
} <span style="color:#006699"><strong>from</strong></span> '<span style="color:#cc3300">dynamsoft-mrz-scanner-bundle-react-native</span>';
<span style="color:#006699"><strong>const</strong></span> handleScan <span style="color:#555555">=</span> <span style="color:#006699"><strong>async </strong></span>() <span style="color:#555555">=></span> {
<span style="color:#cc00ff">setScanState</span>({<span style="color:#330099">kind</span>: '<span style="color:#cc3300">scanning</span>'});
<span style="color:#006699"><strong>try</strong></span> {
<span style="color:#006699"><strong>const</strong></span> <span style="color:#330099">config</span>: MRZScanConfig <span style="color:#555555">=</span> {
<span style="color:#0099ff"><em>// Trial license — network connection required.</em></span>
<span style="color:#0099ff"><em>// Request an extension at:</em></span>
<span style="color:#0099ff"><em>// https://www.dynamsoft.com/customer/license/trialLicense/?product=dcv&package=cross-platform</em></span>
<span style="color:#330099">license</span>:
'<span style="color:#cc3300">LICENSE-KEY</span>',
};
<span style="color:#006699"><strong>const</strong></span> result <span style="color:#555555">=</span> <span style="color:#006699"><strong>await</strong></span> MRZScanner.<span style="color:#cc00ff">launch</span>(config);
<span style="color:#0099ff"><em>// ... handle result</em></span>
} <span style="color:#006699"><strong>catch </strong></span>(<span style="color:#330099">e</span>: unknown) {
<span style="color:#006699"><strong>const</strong></span> message <span style="color:#555555">=</span> e <span style="color:#006699"><strong>instanceof</strong></span> <span style="color:#336666">Error</span> ? e.message : <span style="color:#00aa88"><strong>String</strong></span>(e);
<span style="color:#cc00ff">setScanState</span>({<span style="color:#330099">kind</span>: '<span style="color:#cc3300">error</span>', <span style="color:#330099">code</span>: <span style="color:#555555">-</span><span style="color:#ff6600">1</span>, message});
}
};
</code></span></span></span>
步骤 3:处理扫描结果状态
MRZScanner.launch()解析结果对象,该对象resultStatus映射到以下三个值之一EnumResultStatus。访问之前请检查状态result.data,以区分扫描成功、用户取消或 SDK 级错误。
<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code><span style="color:#006699"><strong>if </strong></span>(result.resultStatus <span style="color:#555555">===</span> EnumResultStatus.RS_FINISHED) {
<span style="color:#cc00ff">setScanState</span>({<span style="color:#330099">kind</span>: '<span style="color:#cc3300">result</span>', <span style="color:#330099">data</span>: result.data <span style="color:#006699"><strong>as </strong></span><span style="color:#336666">Record</span><span style="color:#555555"><</span><span style="color:#006699"><strong>string</strong></span>, <span style="color:#006699"><strong>string</strong></span><span style="color:#555555">></span>});
} <span style="color:#006699"><strong>else</strong></span> <span style="color:#006699"><strong>if </strong></span>(result.resultStatus <span style="color:#555555">===</span> EnumResultStatus.RS_CANCELED) {
<span style="color:#cc00ff">setScanState</span>({<span style="color:#330099">kind</span>: '<span style="color:#cc3300">cancelled</span>'});
} <span style="color:#006699"><strong>else</strong></span> {
<span style="color:#cc00ff">setScanState</span>({
<span style="color:#330099">kind</span>: '<span style="color:#cc3300">error</span>',
<span style="color:#330099">code</span>: result.errorCode <span style="color:#555555">??</span> <span style="color:#555555">-</span><span style="color:#ff6600">1</span>,
<span style="color:#330099">message</span>: result.errorString <span style="color:#555555">??</span> '<span style="color:#cc3300">An unknown error occurred.</span>',
});
}
</code></span></span></span>
步骤 4:在用户界面中显示已解析的 MRZ 字段

当状态为“是”时RS_FINISHED,result.data它是一个包含已解析 MRZ 字段的扁平键值映射。示例应用程序会将高价值字段(例如 `<field1>` firstName、 ` <field2>` lastName、` <field3>`、`<field4>`)提升到“醒目”区域,并将其余字段列在可滚动卡片中。documentNumberdocumentType
<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code><span style="color:#006699"><strong>const</strong></span> HERO_FIELDS <span style="color:#555555">=</span> ['<span style="color:#cc3300">firstName</span>', '<span style="color:#cc3300">lastName</span>', '<span style="color:#cc3300">documentNumber</span>', '<span style="color:#cc3300">documentType</span>'];
<span style="color:#006699"><strong>function</strong></span> <span style="color:#cc00ff">ResultCard</span>({data}: ResultCardProps) {
<span style="color:#006699"><strong>const</strong></span> heroEntries <span style="color:#555555">=</span> HERO_FIELDS.<span style="color:#cc00ff">map</span>(k <span style="color:#555555">=></span> [k, data[k]] <span style="color:#006699"><strong>as </strong></span>[<span style="color:#006699"><strong>string</strong></span>, <span style="color:#006699"><strong>string</strong></span>]).<span style="color:#cc00ff">filter</span>(
([, v]) <span style="color:#555555">=></span> v <span style="color:#555555">!=</span> <span style="color:#006699"><strong>null</strong></span> <span style="color:#555555">&&</span> v <span style="color:#555555">!==</span> '',
);
<span style="color:#006699"><strong>const</strong></span> otherEntries <span style="color:#555555">=</span> <span style="color:#336666">Object</span>.<span style="color:#cc00ff">entries</span>(data).<span style="color:#cc00ff">filter</span>(
([k, v]) <span style="color:#555555">=></span> <span style="color:#555555">!</span>HERO_FIELDS.<span style="color:#cc00ff">includes</span>(k) <span style="color:#555555">&&</span> v <span style="color:#555555">!=</span> <span style="color:#006699"><strong>null</strong></span> <span style="color:#555555">&&</span> v <span style="color:#555555">!==</span> '',
);
<span style="color:#006699"><strong>return </strong></span>(
<<span style="color:#00aa88"><strong>View</strong></span> <span style="color:#330099">style</span>=<span style="color:#aa0000">{</span>styles.resultCard<span style="color:#aa0000">}</span>>
<span style="color:#aa0000">{</span>heroEntries.length <span style="color:#555555">></span> <span style="color:#ff6600">0</span> <span style="color:#555555">&&</span> (
<<span style="color:#00aa88"><strong>View</strong></span> <span style="color:#330099">style</span>=<span style="color:#aa0000">{</span>styles.heroSection<span style="color:#aa0000">}</span>>
<span style="color:#aa0000">{</span>heroEntries.<span style="color:#cc00ff">map</span>(([key, value]) <span style="color:#555555">=></span> (
<<span style="color:#00aa88"><strong>View</strong></span> <span style="color:#330099">key</span>=<span style="color:#aa0000">{</span>key<span style="color:#aa0000">}</span> <span style="color:#330099">style</span>=<span style="color:#aa0000">{</span>styles.heroRow<span style="color:#aa0000">}</span>>
<<span style="color:#00aa88"><strong>Text</strong></span> <span style="color:#330099">style</span>=<span style="color:#aa0000">{</span>styles.heroLabel<span style="color:#aa0000">}</span>><span style="color:#aa0000">{</span><span style="color:#cc00ff">formatLabel</span>(key)<span style="color:#aa0000">}</span></<span style="color:#00aa88"><strong>Text</strong></span>>
<<span style="color:#00aa88"><strong>Text</strong></span> <span style="color:#330099">style</span>=<span style="color:#aa0000">{</span>styles.heroValue<span style="color:#aa0000">}</span>><span style="color:#aa0000">{</span>value<span style="color:#aa0000">}</span></<span style="color:#00aa88"><strong>Text</strong></span>>
</<span style="color:#00aa88"><strong>View</strong></span>>
))<span style="color:#aa0000">}</span>
</<span style="color:#00aa88"><strong>View</strong></span>>
)<span style="color:#aa0000">}</span>
<span style="color:#aa0000">{</span><span style="color:#0099ff"><em>/* other fields omitted for brevity */</em></span><span style="color:#aa0000">}</span>
</<span style="color:#00aa88"><strong>View</strong></span>>
);
}
</code></span></span></span>
步骤 5:操作 Android 硬件返回按钮
在 Android 系统中,硬件返回键默认会退出 React Native 应用。示例应用会拦截此操作,使BackHandler用户返回到空闲状态,而不是在工作流程中途关闭应用。
<span style="color:#212529"><span style="color:#333333"><span style="background-color:#eaeaea"><code><span style="color:#cc00ff">useEffect</span>(() <span style="color:#555555">=></span> {
<span style="color:#006699"><strong>const</strong></span> subscription <span style="color:#555555">=</span> BackHandler.<span style="color:#cc00ff">addEventListener</span>('<span style="color:#cc3300">hardwareBackPress</span>', () <span style="color:#555555">=></span> {
<span style="color:#006699"><strong>if </strong></span>(scanState.kind <span style="color:#555555">!==</span> '<span style="color:#cc3300">idle</span>') {
<span style="color:#cc00ff">setScanState</span>({<span style="color:#330099">kind</span>: '<span style="color:#cc3300">idle</span>'});
<span style="color:#006699"><strong>return</strong></span> <span style="color:#006699"><strong>true</strong></span>; <span style="color:#0099ff"><em>// event handled — prevent default (app exit)</em></span>
}
<span style="color:#006699"><strong>return</strong></span> <span style="color:#006699"><strong>false</strong></span>; <span style="color:#0099ff"><em>// let the system handle it (idle → exit as normal)</em></span>
});
<span style="color:#006699"><strong>return </strong></span>() <span style="color:#555555">=></span> subscription.<span style="color:#cc00ff">remove</span>();
}, [scanState.kind]);
</code></span></span></span>
常见问题和特殊情况
- 试用许可证需要网络连接:每次启动时,捆绑的试用密钥都会与 Dynamsoft 的许可证服务器进行验证。如果设备离线,扫描器将返回错误状态。请将密钥替换为完整许可证以离线使用。
- 在 Android 6 及更高版本上,相机权限被拒绝: React Native 不会
CAMERA在启动时自动请求权限。如果您的应用目标 API 为 23 及更高版本,请确保AndroidManifest.xml在调用MRZScanner.launch()`react-app.getCamera(' ... - TD1卡上的空白结果字段: TD1(信用卡大小的身份证)文件的MRZ区域比TD3护照的MRZ区域窄。请确保光线充足,并将文件平放并完全置于取景器内;部分拍摄会导致字段值为空或格式错误。
结论
本教程演示了如何使用 Dynamsoft MRZ Scanner SDK v3.2 在 React Native 中构建跨平台 MRZ 扫描器。只需一次MRZScanner.launch()调用和简单的结果状态处理,即可在 Android 和 iOS 上实现实时 MRZ 读取,无需编写任何原生代码。后续步骤
更多推荐



所有评论(0)