使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用
Dynamsoft Barcode Reader SDK一款多功能的条码读取控件,只需要几行代码就可以将条码读取功能嵌入到Web或桌面应用程序。这可以节省数月的开发时间和成本。能支持多种图像文件格式以及从摄像机或扫描仪获取的DIB格式。使用Dynamsoft Barcode Reader SDK,你可以创建强大且实用的条形码扫描仪软件,以满足你的业务需求。
慧都网免费下载Dynamsoft Barcode Reader最新版
Electron是最流行的跨平台桌面应用程序框架之一,已在众多著名应用程序中使用。借助网络技术,开发人员可以快速构建适用于Windows,macOS和Linux的产品。在本文中,我们将使用Electron通过Dynamsoft Barcode Reader构建条形码解码和扫描应用程序。
安装
要创建我们的应用,我们需要以下软件/软件包/工具。
- Dynamsoft条码阅读器
- 电子
- npm
- Node.js
- 节点gyp
- 电子重建
- GCC / Clang /其他C ++编译器
Node.js是电子和节点程序包管理的基础。在继续开发工作之前,必须在主机中安装Node.js。
Dynamsoft条码读取器是行业领先的条码解码SDK。它支持各种格式的条形码,并且可以在所有主流平台上运行。
我们将为我们的项目导入一个C ++插件。node-gyp是用于将C ++程序编译为Node.js插件的配置工具。
创建项目
初始化项目
我们使用官方的快速入门示例开始我们的工作。
首先,让我们将示例项目克隆到我们的主机。
git clone https://github.com/electron/electron-quick-start.git
然后,安装依赖项并启动项目以测试我们是否可以正确运行Electron项目。
cd electron-quick-start npm install npm start
如果没有错误,您将看到一个弹出窗口,显示“ Hello World!”。
电子应用程序成功运行
在您的终端中,输入以下命令以安装所需的依赖项。
npm install --save-dev electron-rebuild npm install -g node-gyp
启用Node.js集成
默认情况下,Node.js集成处于禁用状态。我们将其更改为true以便require在导入模块时使用。在main.js中,我们在中指定此值BrowserWindow。
const mainWindow = new BrowserWindow({ width: 1280, height: 1024, webPreferences: { preload: path.join(__dirname, 'preload.js'), nodeIntegration: true // Change to true to use Node API } })
导入Node.js C ++插件
Dynamsoft条形码阅读器提供JavaScript和C / C ++条形码SDK。为了获得Electron应用程序的更好性能,我们可以用C ++编写Node.js条形码插件。我们可以从Github获取插件源代码。
我们libs在项目根目录下创建一个名为文件夹,然后克隆节点条形码存储库。
mkdir libs cd libs git clone https://github.com/Dynamsoft/nodejs-barcode
克隆过程完成后,我们将使用node-gyp更改目录并为我们的项目构建库。
cd nodejs-barcode ../../node_modules/.bin/electron-rebuild
该命令electron-rebuild将检测构建环境并获取相应的标头以完成构建。
![Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用 Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用](https://www.dynamsoft.com/codepool/wp-content/uploads/2020/10/dbr_electron_ubuntu_electron_rebuild_module-1000x175.png)
通过电子重建来重建模块
构建完成后,将创建包含动态链接库的几个目录。
标头和已编译的二进制文件将放在三个文件夹中
由于该库已正确引用了二进制库,因此我们只需要index.js在项目中导入文件即可。
返回项目根目录,并使用编辑器打开main.js文件。main.js是整个应用程序的起点。我们导入条形码附件以进行快速测试。
const dbr = require('dbr')
![Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用 Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用](https://www.dynamsoft.com/codepool/wp-content/uploads/2020/10/dbr_electron_ubuntu_run_wo_errors.png)
需要nodejs-barcode库后未生成任何错误
从文件解码条形码
在开始编码之前,让我们看一下API。
![Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用 Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用](https://www.dynamsoft.com/codepool/wp-content/uploads/2020/10/dbr_electron_node_barcode_apis.png)
Node.js-条形码库的API
- decodeBufferAsync:从原始RGBA数据读取和解码。
- decodeFileAsync:通过读取图像文件进行读取和解码。
- decodeFileStreamAsync:通过读取文件流来读取和解码。
- decodeBase64Async:图像数据作为base64字符串传递
- decodeYUYVAsync:图像数据是RGBA以外的YUV格式
创建解码服务
我们创建两个脚本文件:background-services.js和foreground-services.js。
Web部件使用了前台服务.js,而后台服务.js调用了C ++附加API并提供了解码服务。
在background-services.js中,我们导入Node.js条码插件。
const { ipcMain } = require('electron') const dbr = require('./libs/nodejs-barcode/index') const barcodeTypes = dbr.barcodeTypes function decodeFileAsync(evt, filepath) { } function decodeBase64Async(evt, base64Str) { } function decodeBufferAsync(evt, imgData, width, height) { } function register() { ipcMain.on('decodeFileAsync', decodeFileAsync) ipcMain.on('decodeBase64Async', decodeBase64Async) ipcMain.on('decodeBufferAsync', decodeBufferAsync) } module.exports = { register }
由于Node.js使用事件模型,因此我们使用事件侦听器进行进程间通信。要在主进程上注册侦听器,我们使用ipcMain.on添加事件名称和相应的处理程序。我们将注册过程放在函数中,register以控制何时注册这些事件。
在中foreground-services.js,我们现在可以使用这些服务。
const { ipcRenderer } = require('electron') const DEBUG = false ipcRenderer.setMaxListeners(30) const resultBuffer = { lastUpdate: null, results: [] } const frameBuffer = { lastUpdate: null, imgData: Uint8ClampedArray.from([]), width: 0, height: 0, channel: 0, decoding: false } function setFrameBuffer(img, width, height, channel) { console.log('frame buffer to update') frameBuffer.imgData = img frameBuffer.width = width frameBuffer.height = height frameBuffer.channel = channel frameBuffer.lastUpdate = Date.now() } function startVideoDecode() { frameBuffer.decoding = true videoDecode() } function stopVideoDecode() { frameBuffer.decoding = false } function videoDecode() { ipcRenderer.send('videoDecode', frameBuffer.imgData, frameBuffer.width, frameBuffer.height) } ipcRenderer.on('videoDecode-next', (evt, msg) => { updateResultBuffer(msg) if (frameBuffer.decoding) videoDecode() }) function decodeFileAsync(filepath) { if (DEBUG) console.log('sending decodeFileAsync from renderer process with args: ' + filepath) ipcRenderer.send('decodeFileAsync', filepath) } function decodeBase64Async(base64Str) { if (DEBUG) console.log('sending decodeBase64Async from renderer process') ipcRenderer.send('decodeBase64Async', base64Str) } function decodeBufferAsync(imgData, width, height) { if (DEBUG) console.log('sending decodeBufferAsync from renderer process') ipcRenderer.send('decodeBufferAsync', imgData, width, height ) } function updateResultBuffer(msg) { resultBuffer.lastUpdate = Date.now() resultBuffer.results = msg } ipcRenderer.on('decodeBufferAsync-done', (evt, msg) => { updateResultBuffer(msg) }) ipcRenderer.on('decodeFileAsync-done', (evt, msg) => { updateResultBuffer(msg) }) ipcRenderer.on('decodeBase64Async-done', (evt, msg) => { updateResultBuffer(msg) }) module.exports = { decodeFileAsync, decodeBase64Async, decodeBufferAsync, setFrameBuffer, startVideoDecode, stopVideoDecode, resultBuffer }
消费服务
我们已经准备了服务。现在,我们将注意力转向“前端”。
要在Web应用程序中选择文件,通常使用HTML输入元素。
<input id="file-selector" type="file">
要在选择文件后将文件路径发送到解码服务,我们可以注册该onchange 事件。不要忘记同时注册onclick ,否则它将不会再次解码同一张图像。
document.getElementById('file-selector').onchange = handleFileChange document.getElementById('file-selector').onclick = evt => { evt.target.value = '' } async function handleFileChange(evt) { const file = evt.target.files[0] const results = await services.decodeFileAsync(file.path) updateResults(results) } async function updateResults(results) { // Remove existing results const container = document.getElementById('result-box') container.innerHTML = '' const nodes = [] results.forEach(result => { nodes.push(`<div class="result-card"> \ <p>Format: ${result.format}</p> \ <p>Text: ${result.value}</p> \ </div>` )
![Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用 Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用](https://www.dynamsoft.com/codepool/wp-content/uploads/2020/10/dbr_electron_windows_running-668x1000.png)
显示解码结果
相机实时解码
存取相机
显然,我们的第一步是访问摄像机。类似于在浏览器中激活相机,我们用于navigator.mediaDevices.getUserMedia申请相机访问。
function initCamera() { const video = document.querySelector('video') || document.createElement("video") const navigator = window.navigator const stream = navigator.mediaDevices.getUserMedia({ video: { facingMode: 'user', width: 1280, height: 720 } }) stream.then(stream => { video.srcObject = stream }) }
将getUserMedia返回一个承诺,其将与视频流来解决。我们添加随后的语句以将指定srcObject 为视频元素的解析流。
在中index.html,我们添加一个按钮,并使用指定onclick 事件处理程序initCamera。
<button id="video-capture-btn" class="btn-secondary">Capture</button>
document.getElementById('video-capture-btn').onclick = initCamera
检索图像数据
我们使用画布来获取图像数据。
function getFrame(videoElement) { const cvs = new OffscreenCanvas(640, 360) const ctx = cvs.getContext('2d') ctx.drawImage(videoElement, 0, 0, cvs.width, cvs.height) const imgData = ctx.getImageData(0, 0, cvs.width, cvs.height) decodeFromFrame(imgData) }
画布可以接受视频元素,并使用视频帧绘制图形。只需指定视频元素和要绘制的区域,我们便可以随后获取帧数据。
每次检索视频帧时,都会将其分发到服务以进行解码。
async function decodeFromFrame(frame) { const res = await services.decodeBufferAsync(frame.data, frame.width, frame.height) updateResults(res.data) }
最后,解码过程需要触发一个事件。在中initCamera,我们onplay使用间隔动作注册处理程序,该动作定期执行该getFrame功能以读取和解码图像数据。
派送图像数据
有了图像数据后,我们可以将其发送到解码服务。的ipcMain和ipcRenderer分别是用于主处理和渲染过程的流程间通信的对象。通过注册事件侦听器,每个进程都可以侦听特定的消息。我们注册以下事件及其相应的完成消息协议。
发件人 接收者 活动名称 讯息通讯协定
渲染器 主要 encodeFileAsync 文件路径
主要 渲染器 encodeFileAsync-done 结果
渲染器 主要 encodeBufferAsync 图像数据,宽度,高度
主要 渲染器 encodeBufferAsync-done 结果
渲染器 主要 encodeBase64Async Base64字符串
主要 渲染器 encodeBase64Async-done 结果
主要 渲染器 解码视频 图像数据,宽度,高度
渲染器 主要 下一视频 结果
在后台服务中,我们注册以下侦听器,
ipcMain.on('decodeFileAsync', decodeFileAsync) ipcMain.on('decodeBase64Async', decodeBase64Async) ipcMain.on('decodeBufferAsync', decodeBufferAsync) ipcMain.on('videoDecode', videoDecode)
function videoDecode(evt, imgData, width, height) { if (DEBUG) console.log(`${new Date().toLocaleString()}/real-time decoding for video stream: ${imgData.length/height}, ${width}`) dbr.decodeBufferAsync(imgData, width, height, width*4, barcodeTypes, (err, msg) => { if (err) console.log(err) let results = []; for (index in msg) { let result = Object() let res = msg[index]; result.format = res['format'] result.value = res['value'] results.push(result) } evt.reply('videoDecode-next', results) if (DEBUG) console.log('ipcMain: replied with ' + JSON.stringify(results)) }) } function decodeFileAsync(evt, filepath) { if (DEBUG) console.log('ipcMain: decodeFileAsync invoked: ' + filepath) dbr.decodeFileAsync(filepath, barcodeTypes, (err, msg) => { if (err) console.log(err) let results = []; for (index in msg) { let result = Object() let res = msg[index]; result.format = res['format'] result.value = res['value'] results.push(result) } evt.reply('decodeFileAsync-done', results) if (DEBUG) console.log('ipcMain: replied with ' + JSON.stringify(results)) }) } function decodeBase64Async(evt, base64Str) { if (DEBUG) console.log('ipcMain: decodeBase64Async is invoked') dbr.decodeBase64Async(base64Str, barcodeTypes, (err, msg) => { if (err) console.error(err) let results = []; for (index in msg) { let result = Object() let res = msg[index]; result.format = res['format'] result.value = res['value'] results.push(result) } evt.reply('decodeBase64Async-done', results) if (DEBUG) console.log('ipcMain: replied with ' + JSON.stringify(results)) }) } function decodeBufferAsync(evt, imgData, width, height) { if (DEBUG) console.log('ipcMain: decodeBufferAsync is invoked') console.log(imgData) dbr.decodeBufferAsync(imgData, width, height, width*4, barcodeTypes, (err, msg) => { if (err) console.error(err) let results = []; for (index in msg) { let result = Object() let res = msg[index]; result.format = res['format'] result.value = res['value'] results.push(result) } evt.reply('decodeBufferAsync-done', results) if (DEBUG) console.log('ipcMain: replied with ' + JSON.stringify(results)) }) }
阅读条形码
我们已经实现了我们想要的功能。现在该运行并测试我们的项目了。转到项目根路径,npm start在终端中键入以启动电子条形码读取器。
请注意,如果Electron或平台的版本发生更改,则必须每次编译nodejs-barcode库。
![Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用 Dynamsoft Barcode Reader使用教程:使用Electron和Dynamsoft条形码阅读器创建跨平台条形码阅读器应用](https://www.dynamsoft.com/codepool/wp-content/uploads/2020/10/dbr_electron_windows_initial_sate.png)
启动应用程序后的第一页