JavaScript實(shí)現(xiàn)讀取條碼中的二進(jìn)制數(shù)據(jù)
條碼是一種以機(jī)器可讀的可視形式表示數(shù)據(jù)的方法。條碼種類繁多,主要分為一維碼和二維碼。
我們可以從條碼獲取二進(jìn)制數(shù)據(jù),并通過不同方法去讀碼。例如,在EAN-13中,位于其右邊的代碼1110010代表數(shù)字0。而QR碼,它是二維的,可以存儲更多的數(shù)據(jù),有幾種表示數(shù)據(jù)的模式:
輸入模式 | 模式標(biāo)識 | 最大字符數(shù) | 允許的字符,默認(rèn)編碼 |
---|---|---|---|
僅數(shù)字 | 1 | 7,089 | 0、1、2、3、4、5、6、7、8、9 |
字母與數(shù)字 | 2 | 4,296 | 0-9、A-Z(僅限大寫)、空格、$、%、*、+、-、..、/、: |
二進(jìn)制/字節(jié) | 4 | 2,953 | ISO/IEC 8859-1 |
日語漢字/假名 | 8 | 1,817 | Shift JIS X 0208 |
結(jié)構(gòu)化追加 | 3 | 無限 | 沒有限定 |
PS:結(jié)構(gòu)化追加(Structured Append)是一種將數(shù)據(jù)分到多個(gè)條碼的模式。
在本文中,我們將創(chuàng)建一個(gè)用于讀取條碼二進(jìn)制數(shù)據(jù)的JavaScript庫,并重點(diǎn)處理QR碼,因?yàn)檫@種碼型有多種模式。
Dynamsoft Barcode Reader會(huì)被用于讀取條碼。
新建項(xiàng)目
使用Vite創(chuàng)建一個(gè)新的TypeScript項(xiàng)目:
npm create vite@latest BarcodeDataReader -- --template vanilla-ts
編寫定義
為讀取條碼的二進(jìn)制數(shù)據(jù)定義幾個(gè)接口和一個(gè)枚舉。
定義BarcodeDetail
接口:
export interface BarcodeDetail { mode?:number; //The data encoding mode model?:number; //Number of models errorCorrectionLevel?:number; columns?:number; //The column count rows?:number; //The row count page?:number; //Position of the particular code in structured append codes totalPage?:number; //Total number of structured append codes parityData?:number; //A value obtained by XORing byte by byte the ASCII/JIS values of all the original input data before division into symbol blocks. version?:number; //The version of the code. }
定義Barcode
接口:
export interface Barcode { bytes:Uint8Array; details:BarcodeDetail; barcodeType?:string; }
二進(jìn)制數(shù)據(jù)存儲為Uint8Array
。
定義ReadingResult
接口:
export interface ReadingResult { text?:string; img?:HTMLImageElement; blob?:Blob; }
我們可以得到純文本、HTML圖像元素或blob這三種類型的結(jié)果。
類型需要在DataType
中指定。
export enum DataType { text = 0, image = 1, unknown = 2 }
創(chuàng)建一個(gè)讀取二進(jìn)制數(shù)據(jù)的類
使用以下模板創(chuàng)建一個(gè)新的BarcodeDataReader.ts
文件:
export class BarcodeDataReader{ async read(barcodes:Barcode[],dataType:DataType):Promise<ReadingResult[]>{ if (barcodes.length == 0) { throw new Error("No barcodes given"); } let results:ReadingResult[] = []; return results; } }
根據(jù)不同模式讀取二進(jìn)制數(shù)據(jù)。
async read(barcodes:Barcode[],dataType:DataType):Promise<ReadingResult[]>{ if (barcodes.length == 0) { throw new Error("No barcodes given"); } let results:ReadingResult[] = []; const mode = barcodes[0].details.mode; if (mode == 1) { results = this.readNumericBarcodes(barcodes); }else if (mode == 2) { results = this.readAlphaNumericBarcodes(barcodes); }else if (mode == 4) { results = await this.readByteEncodingBarcodes(barcodes,dataType); }else if (mode == 8) { results = this.readKanjiBarcodes(barcodes); }else if (mode == 3) { results = await this.readStructuredAppendBarcodes(barcodes,dataType); }else { results = await this.readByteEncodingBarcodes(barcodes,DataType.text); } return results; }
實(shí)現(xiàn)對數(shù)字、字母與數(shù)字和日文漢字模式的讀取。這三種模式的數(shù)據(jù)都是純文本,它們可以共享類似的邏輯。
private readAlphaNumericBarcodes(barcodes:Barcode[]):ReadingResult[]{ return this.decodeText(barcodes,"ASCII"); } private readNumericBarcodes(barcodes:Barcode[]):ReadingResult[]{ return this.decodeText(barcodes,"ASCII"); } private readKanjiBarcodes(barcodes:Barcode[]):ReadingResult[]{ return this.decodeText(barcodes,"SHIFT-JIS"); } private decodeText(barcodes:Barcode[],encoding:string){ let results:ReadingResult[] = []; for (let index = 0; index < barcodes.length; index++) { const barcode = barcodes[index]; const decoder = new TextDecoder(encoding); const text = decoder.decode(barcode.bytes); let result = { text:text } results.push(result); } return results; }
以字節(jié)模式讀取數(shù)據(jù)。
由于字節(jié)模式下的數(shù)據(jù)類型未知,我們需要根據(jù)用戶指定的數(shù)據(jù)類型獲取讀取結(jié)果。
如果數(shù)據(jù)類型是文本,我們需要檢測編碼。這里使用chardet
庫進(jìn)行檢測。
private async readByteEncodingBarcodes(barcodes:Barcode[],dataType:DataType):Promise<ReadingResult[]>{ let results:ReadingResult[] = []; for (let index = 0; index < barcodes.length; index++) { const barcode = barcodes[index]; let result:ReadingResult = await this.getResultBasedOnDataType(barcode.bytes,dataType); results.push(result); } return results; } async getResultBasedOnDataType(data:Uint8Array,dataType:DataType) { let result:ReadingResult; if (dataType == DataType.text) { const charset = chardet.analyse(data); const decoder = new TextDecoder(charset[0].name); const text = decoder.decode(data); result = { text:text } }else if (dataType == DataType.image) { const img = await this.getImageFromUint8Array(data); result = { img:img } }else{ result = { blob:this.getBlobFromUint8Array(data) } } return result; } getBlobFromUint8Array(data:Uint8Array) { return new Blob([data]); } getImageFromUint8Array(data:Uint8Array):Promise<HTMLImageElement>{ return new Promise<HTMLImageElement>((resolve, reject) => { const img = document.createElement("img"); const blob = this.getBlobFromUint8Array(data); img.onload = function(){ resolve(img); } img.onerror = function(error) { console.error(error); reject(error); } img.src = URL.createObjectURL(blob); console.log(img.src) }) }
以結(jié)構(gòu)化追加模式讀取數(shù)據(jù)。
需要根據(jù)頁碼對條碼進(jìn)行排序,將多個(gè)碼的數(shù)據(jù)合并成一個(gè)Unit8Array,然后得到結(jié)果。
private async readStructuredAppendBarcodes(barcodes:Barcode[],dataType:DataType):Promise<ReadingResult[]>{ let results:ReadingResult[] = []; barcodes.sort((a, b) => (a.details.page ?? 0) - (b.details.page ?? 0)) let concatedData:Uint8Array = new Uint8Array(); for (let index = 0; index < barcodes.length; index++) { const barcode = barcodes[index]; let merged = new Uint8Array(barcode.bytes.length + concatedData.length); merged.set(concatedData); merged.set(barcode.bytes, concatedData.length); concatedData = merged; } let result = await this.getResultBasedOnDataType(concatedData,dataType); results.push(result); return results; }
讀取測試
然后,我們可以更新index.html
,使用Dynamsoft Barcode Reader讀取QR碼,并使用我們編寫的庫讀取二進(jìn)制數(shù)據(jù)。
以下是基本代碼:
let router = await Dynamsoft.CVR.CaptureVisionRouter.createInstance(); //read barcodes from an image let result = await router.capture(document.getElementById("image"),"ReadBarcodes_Balance"); let dataType = document.getElementById("dataTypeSelect").selectedIndex; let barcodes = []; for (let index = 0; index < result.items.length; index++) { const item = result.items[index]; if (item.type === Dynamsoft.Core.EnumCapturedResultItemType.CRIT_BARCODE) { let data = new Uint8Array(item.bytes.length); data.set(item.bytes); let barcode = { bytes:data, details:item.details } barcodes.push(barcode); } } let results = await dataReader.read(barcodes,dataType) console.log(results);
可以訪問在線演示進(jìn)行試用。
如果手頭沒有二維碼,可以使用此文中的二維碼和二維碼生成器。
下面是讀取兩個(gè)二維碼的測試截圖,一張圖片被編碼在這兩個(gè)二維碼中:
打包為庫
為了便于使用,我們可以將其作為庫發(fā)布到NPM上。
安裝devDependencies
:
npm install -D @types/node vite-plugin-dts
創(chuàng)建一個(gè)新的vite.config.ts
文件:
// vite.config.ts import { resolve } from 'path'; import { defineConfig } from 'vite'; import dts from 'vite-plugin-dts'; // https://vitejs.dev/guide/build.html#library-mode export default defineConfig({ build: { lib: { entry: resolve(__dirname, 'src/index.ts'), name: 'barcode-data-reader', fileName: 'barcode-data-reader', }, }, plugins: [dts()], });
將我們的包的入口點(diǎn)添加到package.json
。
{ "main": "./dist/barcode-data-reader.umd.cjs", "module": "./dist/barcode-data-reader.js", "types": "./dist/index.d.ts", "exports": { "import": { "types": "./dist/index.d.ts", "default": "./dist/barcode-data-reader.js" }, "require": { "types": "./dist/index.d.ts", "default": "./dist/barcode-data-reader.umd.cjs" } }, "files": [ "dist/*.css", "dist/*.js", "dist/*.cjs", "dist/*.d.ts" ] }
運(yùn)行npm run build
。然后,我們可以在dist
文件夾中看到打包好的文件。
源代碼
歡迎下載源代碼并嘗試使用:
github.com/tony-xlh/barcode-data-reader
以上就是JavaScript實(shí)現(xiàn)讀取條碼中的二進(jìn)制數(shù)據(jù)的詳細(xì)內(nèi)容,更多關(guān)于JavaScript讀取條碼二進(jìn)制數(shù)據(jù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript HTML5 Canvas實(shí)現(xiàn)圓盤抽獎(jiǎng)功能
這篇文章主要為大家詳細(xì)介紹了javascript HTML5 Canvas實(shí)現(xiàn)圓盤抽獎(jiǎng)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-04-04深入理解ES6之?dāng)?shù)據(jù)解構(gòu)的用法
本文介紹了深入理解ES6之?dāng)?shù)據(jù)解構(gòu)的用法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01javascript跟隨滾動(dòng)效果插件代碼(javascript Follow Plugin)
這篇文章介紹了javascript跟隨滾動(dòng)效果插件代碼(javascript Follow Plugin),有需要的朋友可以參考一下2013-08-08一文熟練掌握J(rèn)avaScript的switch用法
在JavaScript中switch語句是一種用于多條件分支的控制語句,下面這篇文章主要給大家介紹了關(guān)于如果通過一文熟練掌握J(rèn)avaScript的switch用法的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01JavaScript 設(shè)計(jì)模式 富有表現(xiàn)力的Javascript(一)
javascript設(shè)計(jì)模式是圖靈出版,學(xué)習(xí)中力求每個(gè)章節(jié)都細(xì)看。2010-05-05Typescript定義多個(gè)接口類型聲明的方式小結(jié)
這篇文章主要介紹了Typescript定義多個(gè)接口類型聲明的方式小結(jié),在 TypeScript 中,您可以使用交叉類型(&)或聯(lián)合類型(|)來組合多個(gè)接口,從而實(shí)現(xiàn)多個(gè)接口類型的混合,文中通過代碼講解的非常詳細(xì),需要的朋友可以參考下2025-01-01Array數(shù)組對象中的forEach、map、filter及reduce詳析
這篇文章主要給大家介紹了關(guān)于Array數(shù)組對象中forEach、map、filter及reduce的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用array數(shù)據(jù)具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08數(shù)組Array進(jìn)行原型prototype擴(kuò)展后帶來的for in遍歷問題
不同的程序語言都有多種循環(huán)語句,而且功能是差不多的,當(dāng)然使用場合還是有些區(qū)別的,比如for與for in,for in比較好用,它不需要預(yù)先知道對象屬性的長度。2010-02-02