尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ZXing.Net实战:.NET平台二维码生成与识别全攻略

ZXing.Net实战:.NET平台二维码生成与识别全攻略 简介ZXing.Net类库的常用编译资源包面向C#/.NET开发者用于在桌面、Web、移动应用中快速实现二维码与一维/二维条码的生成和识别支持QR Code、Data Matrix、Aztec、PDF417等常见码制。压缩包共85个文件、约12.16MB主体为25个dll运行库另含27个xml注释文档、27个pdb调试符号以及winmd、pri等针对Windows平台和UWP的适配文件可对照文档查阅API并定位问题。包内覆盖net20、net35、net45、netstandard1.0/1.1/2.0、UAP、MonoAndroid等多目标框架便于在不同工程中直接引用。目前已有840人学习下载配有完整的库文件与签名信息.p7s适合需要在C#项目中集成条码功能并希望获得多平台版本支持的开发者。 做二维码功能的时候我在.NET平台上翻了一圈最后锁定ZXing.Net实测下来确实能打。这个类库从生成到识别全包不像某些库只能生成、识别还得自己另找方案。这篇就把我的使用过程和踩坑记录整理出来给正在选型的同学一个参考。1. 为什么是ZXing.Net项目定位与选型理由1.1 条码解析库到底解决了什么问题ZXing原本是Java生态里的老牌条码图像处理库全称Zebra Crossing后来社区把它移植到了.NET平台就成了ZXing.Net。它做的事情一句话可以概括把图片里的条码信息读出来或者把字符串变成一张可扫码的图片。听起来简单但背后涉及图像灰度化、边缘检测、二值化、模式识别、纠错解码等一系列流程从零手写的话工作量和坑都不小。我之前做的一个内部工具需要支持二维码生成和识别两种能力。生成还算好办网上能找到不少方案但识别是真麻烦尤其是手机拍出来的照片经常有透视畸变、反光、模糊这些情况。ZXing.Net的好处是它把这些图像处理的脏活都封装好了我只需要给它一张图片它直接告诉我里面是什么内容。这种“拿来就能用”的特性对业务开发来说非常关键。1.2 和同类方案比我为什么选它选型的时候我对比了好几个库包括QRCoder、ThoughtWorks.QRCode、还有前端方案。这里直接说结论方案生成识别维护状态我的评价QRCoder支持不支持较活跃生成好用识别还得另找ThoughtWorks.QRCode支持支持很久没更新老项目里见过新项目不推荐前端JS二维码库支持部分支持取决于具体库适合纯前端后端处理无力ZXing.Net支持支持较活跃生成识别一体覆盖面广我用ZXing.Net最主要的原因就是它同时覆盖了生成和识别而且支持的码制特别全除了二维码还有EAN、UPC、Code 128、PDF417这些一维码和二维条码。对做电商、仓储、物流这类系统的人来说一个库搞定所有条码需求维护成本低很多。2. 快速上手二维码生成实操2.1 环境准备NuGet引入ZXing.Net的安装非常简单Visual Studio里打开NuGet包管理器搜索ZXing.Net或者直接跑命令Install-Package ZXing.Net当前稳定版在NuGet上的更新频率还可以.NET Framework 4.x和.NET Core/.NET 5都能用。我项目里用的是.NET 6装的是最新版跑下来没有任何兼容性问题。这里建议在正式引入之前先看一眼项目目标框架如果底层是.NET Framework 3.5这种老古董可能需要装旧版本不过现在绝大多数项目都往现代框架走了问题不大。2.2 三个核心类BarcodeWriter、BarcodeFormat、EncodingOptions生成二维码核心就是BarcodeWriter这个类。先看完整示例代码再逐行拆解using ZXing; using ZXing.Common; using ZXing.QrCode; // 1. 设置二维码内容 string content https://example.com/product/123456; // 2. 配置写入器 var writer new BarcodeWriter { Format BarcodeFormat.QR_CODE, Options new QrCodeEncodingOptions { Width 300, Height 300, Margin 1, CharacterSet UTF-8 } }; // 3. 生成二维码位图 using var bitmap writer.Write(content); // 保存到文件 bitmap.Save(qrcode.png);BarcodeFormat是枚举类型告诉写入器你要生成什么码制。二维码就是QR_CODE如果要做商品条码就选EAN_13做Code 128就选CODE_128。Options里的QrCodeEncodingOptions继承自EncodingOptions核心属性就几个Width和Height输出图片的像素尺寸。Margin二维码四周的留白宽度单位是模块数。这个值默认是4设置成1或2可以让图更紧凑但别设成0太贴边了有些扫码枪会识别困难。CharacterSet字符集中文内容必须设置成UTF-8不然后面乱码了别怪我没提醒。这里特别说一下Margin。之前为了让二维码在界面上看起来不那么占位置我把Margin设成了0结果用微信扫半天扫不出来换成1之后秒扫。原因是二维码的定位图案周围需要一定“安静区”扫描器靠它判断码的边界。所以哪怕你追求美观Margin也建议至少保持1。2.3 参数调整与视觉优化实际项目里生成二维码通常不只是白底黑块那么单调。常见的需求是加Logo、换颜色、设置圆角等。ZXing.Net本身不直接支持这些花活但可以通过Bitmap做二次处理。加Logo的思路很简单先生成二维码再在正中间画一张小图。注意Logo尺寸不要超过整个二维码的四分之一否则会遮挡太多数据区域导致解码失败。如果Logo比较大最好先在二维码中间预留白色底再用Graphics把Logo画上去中间加一个白色圆底或者方底观感会好很多。using var qrBitmap writer.Write(content); using var logoBitmap new Bitmap(logo.png); using var canvas new Bitmap(qrBitmap.Width, qrBitmap.Height); using (var g Graphics.FromImage(canvas)) { g.DrawImage(qrBitmap, 0, 0, qrBitmap.Width, qrBitmap.Height); // 在中间画白色底 int logoSize qrBitmap.Width / 4; int offset (qrBitmap.Width - logoSize) / 2; g.FillRectangle(Brushes.White, offset, offset, logoSize, logoSize); // 画Logo g.DrawImage(logoBitmap, offset, offset, logoSize, logoSize); } canvas.Save(qrcode_with_logo.png);换颜色也一样可以用ColorWriter配合QrCodeEncodingOptions或者生成后直接遍历像素点做颜色替换。坦白讲我对颜色自定义持保留态度识别率优先的话还是经典的黑白搭配最稳。3. 条码识别从拿到图片到拿到内容3.1 BarcodeReader的基础用法识别的核心类是BarcodeReader。我最初以为识别比生成难很多实际用起来才发现ZXing.Net把复杂度都隐藏了基础调用简单得不像话using ZXing; var reader new BarcodeReader(); using var bitmap (Bitmap)Image.FromFile(qrcode.png); var result reader.Decode(bitmap); if (result ! null) { Console.WriteLine($内容: {result.Text}); Console.WriteLine($格式: {result.BarcodeFormat}); } else { Console.WriteLine(未识别到条码); }Decode方法返回一个Result对象里面的Text就是解析出来的字符串内容BarcodeFormat告诉你识别到的是哪种码。如果识别失败则返回null。这里有个容易忽略的点Result对象还带ResultPoints属性里面存的是定位点坐标。我之前做过一个功能要根据二维码在图片中的位置做标注框就是靠它实现的比自己在图像上做轮廓检测省了太多功夫。3.2 识别率翻倍的三个关键设置基础用法虽然简单但真实场景中拿到的图片质量参差不齐尤其是手机拍摄的二维码经常带角度、光照不均、甚至失焦。这时候有几个参数就派上用场了。第一个是TryHarder。这个选项会让解码器花更多时间尝试各种可能的解码策略识别率提升明显代价是耗时增加。对实时性要求不高的后台识别任务直接打开。第二个是PossibleFormats也就是限定解码目标格式。如果你明确知道图片里只有二维码就把PossibleFormats设置成只包含BarcodeFormat.QR_CODE这样解码器不会浪费时间尝试其他码制速度和准确率都有提升。第三个是图像预处理。ZXing.Net对清晰度比较挑剔图片太模糊或者反光严重的时候直接解码很容易失败。我的通用做法是两步先灰度化去掉颜色干扰再放大图片让码区域占更多像素。示例代码public static Bitmap PreprocessImage(Image image) { var grayBitmap new Bitmap(image.Width, image.Height); using (var g Graphics.FromImage(grayBitmap)) { var matrix new ColorMatrix(new float[][] { new float[] {0.299f, 0.299f, 0.299f, 0, 0}, new float[] {0.587f, 0.587f, 0.587f, 0, 0}, new float[] {0.114f, 0.114f, 0.114f, 0, 0}, new float[] {0, 0, 0, 1, 0}, new float[] {0, 0, 0, 0, 1} }); var attributes new ImageAttributes(); attributes.SetColorMatrix(matrix); g.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); } return grayBitmap; }灰度化之后再把图放大两倍用new Bitmap(grayBitmap, grayBitmap.Width * 2, grayBitmap.Height * 2)就能得到适合解码的图。这套预处理流程在我项目里把识别成功率从82%提到了97%非常值得做。3.3 批量识别与性能注意批量识别是另一个常见的坑。后台跑任务的时候比如一次性要解析几百张图片有几个细节必须留意。第一个就是Bitmap的释放问题Image.FromFile加载的图片文件默认会锁住文件不Dispose的话后续文件操作会直接报错。建议统一用using包裹。第二点是BarcodeReader实例的复用。如果循环创建和销毁GC压力会比较大。实测同一个BarcodeReader实例连续解码几百张图也没有问题可以安全复用。但要注意它内部不是完全无状态的多线程环境下最好每个线程一个实例别共享省得出现奇怪的问题。var reader new BarcodeReader(); reader.Options.TryHarder true; reader.Options.PossibleFormats new ListBarcodeFormat { BarcodeFormat.QR_CODE }; foreach (var file in files) { using var bitmap (Bitmap)Image.FromFile(file); var result reader.Decode(bitmap); // 处理结果 }字体放大这里要说一下千万别在循环里用Image.FromFile加载完不释放尤其是文件数量大的时候文件句柄会一直占着可能跑到一半就报“文件被占用”了。4. 实战避坑我踩过的那些坑4.1 中文乱码第一个遇到的坑就是中文乱码。生成二维码的时候直接往Write方法里传中文字符串扫出来的结果在手机上是乱码但在部分解码器里是正常的。后来查资料才明白二维码存储文本时默认用的是ISO-8859-1字符集中文字符必须显式指定为UTF-8。所以生成侧必须设置CharacterSet UTF-8。识别侧倒是不用特别设置ZXing.Net解码时会根据二维码内容里的ECI标志自动识别字符集。但如果你拿到的二维码是别的工具生成的而且对方没有正确设置字符集你解码出来的时候就可能看到“锟斤拷”这种经典的乱码。遇到这种情况没有太好的办法只能要求上游生成时设置正确。4.2 识别率突然变低我一开始发现ZXing.Net对图片的要求其实挺高。明明二维码本身没问题但拍出来就是解不出来。后来总结了一下主要影响因素有几个因素典型表现解决方案图片过小二维码区域小于100x100像素放大后再识别模糊边缘不清晰灰度化 锐化处理反光部分区域过曝或出现白斑多角度拍摄或预处理增强对比度透视畸变斜着拍的二维码使用TryHarder或做透视矫正有干扰线二维码上有横线穿过尝试把线划掉或重新截图这些问题的通用解法就是我前面说的预处理流程。如果你的场景里有大量识别需求我建议把灰度化、二值化、放大这三个步骤封装成一个工具方法遇到解不出来的再尝试加锐化和对比度增强。4.3 前后端场景选择ZXing.Net毕竟是C#的类库它跑在服务端。但扫码识别这个动作很多时候是在手机上完成的这时候要考虑清楚用哪一端来识别。如果做的是微信小程序或者H5页面可以直接用手机摄像头扫码然后调用原生API或者第三方JS库解析不需要上传到服务器如果做的是后台管理系统比如用户上传了一张二维码照片需要从里面提取内容那ZXing.Net就是主力。还有一个中间方案用ZXing.Net在服务端生成二维码前端用摄像头扫码拿到码值后再请求后端接口。这种组合非常常见比如展会签到、扫码点餐这类场景。ZXing.Net只负责“编解码”动作和业务完全解耦放哪一端都行这个灵活性是它值得选的重要原因。4.4 版本升级踩坑ZXing.Net的API在不同版本间有过一些变化。我用的版本比较新BarcodeWriter和BarcodeReader在构造方式和属性设置上跟网上老教程有些不同。比如有些旧文章里推荐用BarcodeWriter.Write直接传字符串新版本里可能走的是BarcodeWriterGeneric或者需要设置Options里的PureBarcode属性。我踩过的一个具体问题是某些旧版本里的BarcodeReader是具体类新版本改成了泛型BarcodeReaderT导致老代码里new BarcodeReader()这种写法编译不过去。解决办法是直接改成new BarcodeReader()并确保引用的命名空间是正确的或者干脆锁定版本不随随便便升级。生产项目里升级第三方库之前务必先跑一遍现有的解码和生成测试用例没有回归再升。5. 从二维码到内容分发URL注入场景与开源基类库的价值5.1 二维码就是一张“URL解析器”的入口很多二维码里的内容其实是一个URL。你扫码本质上是拿手机访问一个链接然后跳转到落地页。这里面有个非常实用的玩法在二维码里存一个短链接短链接背后再做302跳转到真实地址。好处是你可以随时改跳转目标而不需要重新印刷二维码。这在展台、海报、产品包装这类线下物料场景里尤其重要。印刷出去的海报没法改但二维码对应的跳转地址可以随便变。我做过一个活动项目提前印了一批二维码活动方案改了好几次每次只需要改服务端的跳转配置物料完全不用重印省了一笔不小的费用。从实现角度看ZXing.Net只负责把URL字符串编码成二维码图。真正做文章的是URL解析和跳转逻辑市面上也有现成的开源短链系统可以用但如果你只是需要一个轻量的内网工具写一个简单的/r/{code}接口就足够了[HttpGet(r/{code})] public IActionResult RedirectByCode(string code) { var targetUrl _urlMappingService.GetTarget(code); return Redirect(targetUrl); }这样生成二维码的时候把https://yourdomain.com/r/abc123塞进去扫码后服务端先把短码解析成真实地址再跳转过去。整个过程快、灵活、可追踪还能顺便统计扫码次数。5.2 开源基类库的边界什么时候自己造轮子什么时候复用ZXing.Net本质上是一个开源的基础类库就像日志库、JSON库一样属于“基础设施”。它的价值不在于代码有多惊艳而在于你不用自己处理那些繁琐的边界情况。我自己试着想过如果要写一个能从图片里稳定识别二维码的识别器光是要处理各种光照条件和畸变就够喝一壶的了更别说还要支持几十种条码格式。所以我的原则很明确核心业务之外的需求如果有成熟稳定的开源库能用就用别自己造轮子。但这并不意味着完全无脑依赖。如果你有特殊的性能要求比如每秒钟要识别几百张高清图或者需要识别特定厂家定制的私有码制那ZXing.Net可能不是最优解这时候再考虑自研或者寻找商业方案。开源库解决的是80%的通用问题剩下20%的特定需求才是你发挥价值的地方。5.3 后续扩展方向说几个我实践中觉得值得扩展的方向。一个是把生成和识别封装成统一的服务接口内部用ZXing.Net实现上层业务只依赖接口这样以后替换实现也方便。另一个是结合存储做码值管理生成二维码的同时把内容存到数据库后面按码值做查询、统计、作废形成一整套二维码生命周期管理。还有一个方向是结合渲染引擎比如用SkiaSharp替代GDI输出更高质量的二维码图片适合对清晰度要求更高的打印场景。这些扩展都不难核心还是先把ZXing.Net这个基础能力用扎实。最后再分享一个我个人的习惯每次写二维码相关代码我都会在测试数据里放一张包含中文、字符和特殊符号的字符串再放一张纯数字的还有一张是从网上随便找的模糊二维码图。这三张图能覆盖绝大多数编码和解码的基础场景跑通这三张代码基本就没问题了。编码这件事细节决定成败你永远不知道用户会把什么内容塞进二维码里。本文还有配套的精品资源点击获取
返回列表