在“”这篇文章中,我们通过“成像”、“截图”与“识别”三个步骤介绍了使用扫码的主要过程及注意事项。本文主要针对“识别”的过程,对Barcode的概念作出一个较为简单的介绍,同时也会讨论ZXing的使用方法。
是一个Java实现的开源库,可以支持多种Barcode的识别和生成,同时也为其它的语言提供了相应的接口,比如C++、Python、.Net等等。这里,我们使用,可以很方便的集成在UWP环境中,便于扫码工作的开发。
Barcode简介
Barcode是一种机器可读的数据表示方式,作为某种商品或产品的唯一标识。通常被翻译成条码,但我个人并不喜欢这种翻译,因为不是所有的barcode都是条形的。当然,这应该是有历史原因的。Barcode的形式多种多样,按照它们的外观,可以分为Linear barcode(一维码)和Matrix barcode(二维码)。你可能认为你对它们都有所了解,因为它们大概都是这个样子的:
但事实上,它们有甚至有可能是这样子的:
我们通常所说的二维码和上文提到的二维码有所不同,只是Matrix barcode的一种,叫做QR code。举这些例子只是为了说明Barcode种类繁多,有些编码格式并不常用,即使是ZXing也没有做到所有格式的支持,开发者只需了解就好。
Code 39、Code 128、EAN、UPC、QR Code是我们生活中能经常见到的几种编码格式,同时ZXing对几种格式都有比较好的支持。其中,UPC-A是一种国际通用的编码格式,由12个数字构成,EAN-13是在UPC-A基础上的一种扩充(多了一个数字)。快数一数你身边的薯片的编码是不是13位!如果是的话,它最前边的两个数字是不是“69”?在EAN-13的编码体系中,前三位数字表示商品生产商的国家(并不是商品所属公司的国家),中国的编码是690~699,美国是(000~019、030~039、060~139),日本是(450~459、490~499),and so on。不同的编码格式通常用在不同的领域,如果你看到了一个Code 39或者Code 128的Barcode,那么这很就可能是一个快递编码,这个时候你就可以去那些提供快递查询的网站查询一下你的快递信息,如果有API提供出来那就更是再好不过了。至于QR Code,就是我们经常用手机扫一扫的二维码,表示的信息更是多种多样,并不仅仅是一个url那么简单,至于这些信息如何处理,是我们一会儿将要讨论的内容。
配置工程
在提到,可以在Visual Studio环境下,通过nuget管理器安装ZXing.Net,如果你仅仅希望通过扫码得到一个扫码结果(比如字符串,或者是一个链接),当然可以这样做,而且过程十分简单。但这里我们并不推荐这种方法,因为从NuGet上得到的package只提供了一些简单的API接口,并没有将ZXing.Net的API全部提供出来,至于为什么这么做,说实话,我也不知道,可能是为了稳定性等原因。如果你是一个比较执着的programmer,也可以在使用NuGet包的情况下,自己实现其它丰富的功能,毕竟有ZXing的源代码可以参考,这就是开源的好处!在这篇文章中,我们还是以简单为主,毕竟ZXing.Net已经为我们提供了编译好的动态连接库,有什么理由让我们不用呢?
Important 官网提供的库和NuGet提供的库API稍微有些不同,如果需要更换,需要稍作修改,但过程并不是很繁琐。
你可以从下载ZXing.Net的Release,压缩包目录下的/winrt/zxing.winrt.dll即是我们希望添加到工程的引用。
然后,把这个reference添加到相应的工程里就行:
获取Barcode内容信息
首先,你可以按照“”这篇文章中提供的步骤,初始化摄像头、设置Timer并添加Tick事件,在Tick事件中,通过调用ZXing.Net的API获取扫码结果。
private async void TimerTick(object sender, object e){ try { if (_mediaCapture == null) return; var props = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview); uint width = 0, height = 0; if (props is ImageEncodingProperties) { width = (props as ImageEncodingProperties).Width; height = (props as ImageEncodingProperties).Height; } else if (props is VideoEncodingProperties) { width = (props as VideoEncodingProperties).Width; height = (props as VideoEncodingProperties).Height; } System.Diagnostics.Debug.WriteLine("Start scanning..."); Result decodeResult = null; using (VideoFrame frameFormat = new VideoFrame(BitmapPixelFormat.Rgba8, (int)width, (int)height)) { using (var frame = await _mediaCapture.GetPreviewFrameAsync(frameFormat)) { var buffer = new Windows.Storage.Streams.Buffer( (uint)(4 * frame.SoftwareBitmap.PixelWidth * frame.SoftwareBitmap.PixelHeight)); frame.SoftwareBitmap.CopyToBuffer(buffer); var reader = new BarcodeReader(); decodeResult = reader.Decode(buffer.ToArray(), (int)width, (int)height, RGBLuminanceSource.BitmapFormat.RGB32); System.Diagnostics.Debug.WriteLine(decodeResult); if (decodeResult != null) { await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { resultTextBlock.Text = decodeResult.Text; }); } } } //You can handle decodeResult by other ways. if (decodeResult != null) await Windows.System.Launcher.LaunchUriAsync(new Uri(decodeResult.Text)); } catch (ArgumentNullException ane) { System.Diagnostics.Debug.WriteLine(ane.ToString()); } catch (UriFormatException ufe) { System.Diagnostics.Debug.WriteLine(ufe.ToString()); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine("Timer tick exception: {0}!", ex); }}
得到的Result包含两个重要的属性: Text和BarcodeFormat。其中,Text就是相关Barcode表示的文本信息,而BarcodeFormat则是对应的编码方式。你可能会问,知道内容就好了,要编码方式有什么用?这里有个问题是,同样的一段文本信息(尤其是纯数字信息)在不同的编码方式下可能会有不同的含义(比如两种完全不同的商品)。
解析内容
如同前面所说,Barcode可以表示各种各样的信息,可能是“Hello UWP!”这样的纯文本,也可能是图书的ISBN编码,或者是一个网络上的URL连接。这里,我们可以通过编码类型来判断,因为不同的编码类型应用场景不一样嘛,许多一维码根本就不支持非数字文本。此外,ZXing也为我们提供的方便的接口(NuGet上的ZXing.Net并没有),能标识出文本的具体内容:
ParsedResult parsedResult = ResultParser.parseResult(result);
parsedResult包含以下几种信息:
//// Summary:// Represents the type of data encoded by a barcode -- from plain text, to a URI,// to an e-mail address, etc.public enum ParsedResultType{ ADDRESSBOOK = 0, EMAIL_ADDRESS = 1, PRODUCT = 2, URI = 3, TEXT = 4, GEO = 5, TEL = 6, SMS = 7, CALENDAR = 8, WIFI = 9, ISBN = 10, VIN = 11}
后续工作
有了以上的内容,我们就可以对信息作进一步的处理,这就需要依赖于第三方服务提供的API接口了。
快递:
“快递100”为用户提供了一个非常方便的页面,通过快递条码就可以轻松查询到快递信息。
如果想要自定义页面,则推荐使用其API:
商品:
亚马逊为其网站的商品提供了商品搜索的,通过提供条码Id及其类型,则可以查询到相应商品信息。
在ItemLookup页面中,填写一些查询的基本信息,Marketplace可供用户选择查询的数据源,webservices.amazon.com为美国的市场,中国市场可以选择webservices.amazon.cn。下面的三个参数需要用户注册亚马逊的AWS服务(注册过程相当复杂╮(╯_╰)╭)。
在ItemLookup的过程,设置ItemId(code信息)和IdType(这里可以选择ASIN、SKU、UPC、EAN、ISBN),然后点击Run request就可以生成Request请求的URL,并且能获取到响应的服务器Response——一个xml文档,包含商品的各种信息。
Signed Request URL:
http://webservices.amazon.com/onca/xml?AWSAccessKeyId=********************&AssociateTag=************&IdType=UPC&ItemId=431604028525&Operation=ItemLookup&ResponseGroup=Images%2CItemAttributes%2COffers&SearchIndex=All&Service=AWSECommerceService&Timestamp=2016-05-30T12%3A49%3A51.000Z&Signature=**********************************************
Response:
f90faa33-3fb2-4fd7-a885-abe33ea6967f 0.1724468780000000 True UPC 431604028525 Images ItemAttributes Offers All All B008BO4D9G http://www.amazon.com/Nature-Made-Pharmacist-Recommended-Concentrate/dp/B008BO4D9G%3FSubscriptionId%3DAKIAIZNMTNCCU6OFZG4A%26tag%3Dfreshomer-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3DB008BO4D9G Technical Details http://www.amazon.com/Nature-Made-Pharmacist-Recommended-Concentrate/dp/tech-data/B008BO4D9G%3FSubscriptionId%3DAKIAIZNMTNCCU6OFZG4A%26tag%3Dfreshomer-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB008BO4D9G Add To Baby Registry http://www.amazon.com/gp/registry/baby/add-item.html%3Fasin.0%3DB008BO4D9G%26SubscriptionId%3DAKIAIZNMTNCCU6OFZG4A%26tag%3Dfreshomer-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB008BO4D9G Add To Wedding Registry http://www.amazon.com/gp/registry/wedding/add-item.html%3Fasin.0%3DB008BO4D9G%26SubscriptionId%3DAKIAIZNMTNCCU6OFZG4A%26tag%3Dfreshomer-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB008BO4D9G Add To Wishlist http://www.amazon.com/gp/registry/wishlist/add-item.html%3Fasin.0%3DB008BO4D9G%26SubscriptionId%3DAKIAIZNMTNCCU6OFZG4A%26tag%3Dfreshomer-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB008BO4D9G Tell A Friend http://www.amazon.com/gp/pdp/taf/B008BO4D9G%3FSubscriptionId%3DAKIAIZNMTNCCU6OFZG4A%26tag%3Dfreshomer-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB008BO4D9G All Customer Reviews http://www.amazon.com/review/product/B008BO4D9G%3FSubscriptionId%3DAKIAIZNMTNCCU6OFZG4A%26tag%3Dfreshomer-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB008BO4D9G All Offers http://www.amazon.com/gp/offer-listing/B008BO4D9G%3FSubscriptionId%3DAKIAIZNMTNCCU6OFZG4A%26tag%3Dfreshomer-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB008BO4D9G http://ecx.images-amazon.com/images/I/51j%2BGzAmXTL._SL30_.jpg 30 15 http://ecx.images-amazon.com/images/I/51j%2BGzAmXTL._SL75_.jpg 75 37 http://ecx.images-amazon.com/images/I/51j%2BGzAmXTL._SL75_.jpg 75 37 http://ecx.images-amazon.com/images/I/51j%2BGzAmXTL._SL110_.jpg 110 54 http://ecx.images-amazon.com/images/I/51j%2BGzAmXTL._SL160_.jpg 160 79 http://ecx.images-amazon.com/images/I/51j%2BGzAmXTL.jpg 500 247 http://ecx.images-amazon.com/images/I/51C8Vnbv71L._SL30_.jpg 30 19 http://ecx.images-amazon.com/images/I/51C8Vnbv71L._SL75_.jpg 75 48 http://ecx.images-amazon.com/images/I/51C8Vnbv71L._SL75_.jpg 75 48 http://ecx.images-amazon.com/images/I/51C8Vnbv71L._SL110_.jpg 110 71 http://ecx.images-amazon.com/images/I/51C8Vnbv71L._SL160_.jpg 160 103 http://ecx.images-amazon.com/images/I/51C8Vnbv71L.jpg 500 321 http://ecx.images-amazon.com/images/I/31m-odZf5HL._SL30_.jpg 30 22 http://ecx.images-amazon.com/images/I/31m-odZf5HL._SL75_.jpg 75 56 http://ecx.images-amazon.com/images/I/31m-odZf5HL._SL75_.jpg 75 56 http://ecx.images-amazon.com/images/I/31m-odZf5HL._SL110_.jpg 110 82 http://ecx.images-amazon.com/images/I/31m-odZf5HL._SL160_.jpg 160 120 http://ecx.images-amazon.com/images/I/31m-odZf5HL.jpg 500 375 Health and Beauty Nature Made 474911 0031604028527 0031604028527 0783318987897 0431604028525 USP VERIFIED - United States Pharmacopeia (USP) sets standards for medicines, food ingredients, and dietary supplements worldwide. Nature Made was the first supplement product to be verified by USP, including fish oil supplements. FOOD EQUIVALENT - Two fish oil pills contain the same amount of omega-3 fatty acids of two servings of fish. SOURCE AND PURITY - Comes from deep ocean water fish (NOT FARM RAISED) and state-of-the-art purification processes remove mercury. Omega-3 fish oil helps support a healthy heart. 2 BOTTLES OF 200 GEL TABS EACH -There are 200 fish oil capsules in each of two bottles, for a total of 400 softgels. HIGH QUANTITY DOSAGE - Nature Made Fish Oil daily suggested usage contains 2,400 mg of fish oil with 720 mg of Omega-3 Fatty Acids (including 360 mg EPA and 240 mg DHA). 0 313 613 174 625 Nature Made 474911 1 320 630 180 620 1 474911 Health and Beauty HEALTH_PERSONAL_CARE Nature Made 400 Softgels Nature Made Nature Made Fish Oil (400 Softgels) Pharmacist Recommended Fish Oil Pills (2400mg Fish Oil Concentrate, 720mg of Omega 3 Fatty Acids, 360mg EPA, 240 mg DHA) 431604028525 431604028525 783318987897 031604028527 1560 USD $15.60 64 0 0 0 1 1 http://www.amazon.com/gp/offer-listing/B008BO4D9G%3FSubscriptionId%3DAKIAIZNMTNCCU6OFZG4A%26tag%3Dfreshomer-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB008BO4D9G New lt2bT68NjTRG7AuG%2FAAqcBmiJtLextibUqq62er5gQHN%2FtsGphHchT6YZGwo9DRl4CDw8UuCEQki2TAilIIVkKZ03DwP194gQLXxDlAXT%2BvPLPifatvCMnSk9skoDe%2FETTBJg3o7sdO%2B%2FevV0UWM6Q%3D%3D 2135 USD $21.35 Usually ships in 24 hours now 0 0 0 1
当然,这只是Amazon为我们提供的一个开发工具,在真正开发过程中,需要用code来构造Amazon的数据请求。
一些数据常量:
private const string AWS_ACCESS_KEY_ID = "********************";private const string AWS_SECRET_KEY = "****************************************";private const string AWS_ASSOCIATE_TAG = "************";private const string FORMAT_SORTABLE_TIME = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.000Z'";private static readonly string HASH_ALGORITHM_NAME = MacAlgorithmNames.HmacSha256;private const string ENDPOINT_ITEM_SEARCH_BY_PRODUCT_ID = "http://webservices.amazon.com/onca/xml?AWSAccessKeyId={0}&AssociateTag={1}&IdType={2}&ItemId={3}&Operation=ItemLookup&ResponseGroup=Images%2CItemAttributes%2COffers&SearchIndex=All&Service=AWSECommerceService&Timestamp={4}&Signature={5}";private const string FORMAT_STRING_TO_SIGN = "GET\nwebservices.amazon.com\n/onca/xml\nAWSAccessKeyId={0}&AssociateTag={1}&IdType={2}&ItemId={3}&Operation=ItemLookup&ResponseGroup=Images%2CItemAttributes%2COffers&SearchIndex=All&Service=AWSECommerceService&Timestamp={4}";
通过ItemId和IdType获取商品信息:
public static async TaskGetProductsById(String id, String idType){ try { String time = DateTime.Now.ToUniversalTime().ToString(FORMAT_SORTABLE_TIME); String stringToSign = String.Format(FORMAT_STRING_TO_SIGN, AWS_ACCESS_KEY_ID, AWS_ASSOCIATE_TAG, idType, id, WebUtility.UrlEncode(time)); IBuffer buffMsg; CryptographicKey hmacKey; IBuffer buffHMAC; String hmac = createHMAC(stringToSign, HASH_ALGORITHM_NAME, AWS_SECRET_KEY, out buffMsg, out hmacKey, out buffHMAC); String url = String.Format(ENDPOINT_ITEM_SEARCH_BY_PRODUCT_ID, AWS_ACCESS_KEY_ID, AWS_ASSOCIATE_TAG, idType, id, WebUtility.UrlEncode(time), WebUtility.UrlEncode(hmac)); HttpClient httpClient = new HttpClient(); HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, new Uri(url)); req.Headers.AcceptEncoding.Clear(); HttpResponseMessage resp = await httpClient.SendRequestAsync(req); String responseStr = ""; if (resp.IsSuccessStatusCode) { IBuffer buf = await resp.Content.ReadAsBufferAsync(); responseStr = Encoding.UTF8.GetString(buf.ToArray()); } if (String.IsNullOrEmpty(responseStr)) return null; //Get data form xml string. LookupResponse response = null; try { using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(responseStr))) { XmlSerializer xmlSerializer = new XmlSerializer(typeof(LookupResponse)); response = xmlSerializer.Deserialize(stream) as LookupResponse; } } catch (Exception) { return null; } if (response == null || response.Items == null || response.Items.Count <= 0) return null; return response.Items[0]; } catch { return null; }}
中间有一个签名的过程需要用到HMAC的方法:
private static String createHMAC(String strMsg, String strAlgName, String key, out IBuffer buffMsg, out CryptographicKey hmacKey, out IBuffer buffMAC){ MacAlgorithmProvider objMacProv = MacAlgorithmProvider.OpenAlgorithm(strAlgName); String strNameUsed = objMacProv.AlgorithmName; BinaryStringEncoding encoding = BinaryStringEncoding.Utf8; buffMsg = CryptographicBuffer.ConvertStringToBinary(strMsg, encoding); IBuffer buffKeyMaterail = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf8); hmacKey = objMacProv.CreateKey(buffKeyMaterail); buffMAC = CryptographicEngine.Sign(hmacKey, buffMsg); return CryptographicBuffer.EncodeToBase64String(buffMAC);}
经过这些过程,就可以拿到Amazon的数据了,至于sample code中的LookupResponse如何构造,请参考AWS API以及Response返回数据。
后记
关于Barcode的处理还要看实际的应用场景,本文只是通过举例提到针对某些场景的处理方式,欢迎大家多多交流经验。
关于淘宝是否有对外开放的API,我本人并没有调研或者使用过,如果有的话,希望大家分享一下开发经验,谢谢!
Links