条码生成工具(Aegis.Toolkit.Barcode)
Aegis.Toolkit.Barcode 用来生成二维码和一维条形码,输出黑白 PNG。编码基于 ZXing.Net,PNG 编码只依赖 .NET 基类库。纯静态类调用,不需要 DI 注册,也不需要 Component.deps.json 声明。
组件概览
| 字段 | 说明 |
|---|---|
| 组件名称 | 条码生成工具 |
| 真实类库 | Aegis.Toolkit.Barcode |
| 组件定位 | 二维码/一维条形码生成工具包 |
| 引入方式 | 安装 NuGet,引用命名空间后直接调用静态类 |
是否需要 Component.deps.json | 否 |
| 核心能力 | QrCodeGenerator、BarcodeGenerator,各含 ToPng / ToBase64 / ToDataUri / WriteTo |
| 典型配套 | 无,任意 Aegis 服务或独立项目均可引用 |
什么时候要用它
适合场景:
- 页面或报表里要展示二维码/条形码(挂号单、检验报告、收银小票、资产标签)
- 接口要把条码图片回给前端(PNG 字节或 data URI)
- 要把条码写进文件或嵌入 PDF 附件
不适合场景:
- 识别、解码条码(本包只生成,不读取)
- 彩色条码、Logo 合成、矢量格式(SVG)输出(拿 PNG 字节自行后处理)
最小可运行路径
第一步:安装包
dotnet add package Aegis.Toolkit.Barcode --version 3.1.0
第二步:直接调用
using Aegis.Toolkit.Barcode;
// 二维码:默认纠错 Medium、每模块 4px、四周留白 4 模块
byte[] qrPng = QrCodeGenerator.ToPng("https://example.com/report/1001");
// 一维条形码:默认窄条 2px、条高 60px、左右留白 20px
byte[] barPng = BarcodeGenerator.ToPng("Aegis-123", BarcodeSymbology.Code128);
没有第三步。不注册 DI,不写配置文件。
Service 封装:Base64 与文件流两套标准用法
业务项目里按框架惯例封到 Service 层:契约继承 IBusinessService 放 Contract 工程,实现放 Services 工程,BusinessServices 自动注册。渲染选项集中在实现里维护。
方案一:Base64
using Aegis.Services;
using Aegis.Toolkit.Barcode;
public interface IBarcodeService : IBusinessService
{
/// <summary>二维码 PNG 的 Base64</summary>
string GetQrBase64(string content);
}
public class BarcodeService : IBarcodeService
{
private readonly QrCodeOptions _qrOptions = new() { ModulePixels = 6 };
public string GetQrBase64(string content)
=> QrCodeGenerator.ToBase64(content, _qrOptions);
}
控制器 SuccessResult(_barcodeService.GetQrBase64(content)) 回给前端,拼上 data:image/png;base64, 前缀即可放进 img;想免拼接,把 ToBase64 换成 ToDataUri。
方案二:文件流
public interface IBarcodeService : IBusinessService
{
/// <summary>把二维码 PNG 写入流(不关闭、不刷新传入的流)</summary>
void WriteQrTo(string content, Stream stream);
}
流的去向由调用方决定,两种常见走法:
// 自行落盘
await using var fileStream = new FileStream("qr-1001.png", FileMode.Create, FileAccess.Write);
_barcodeService.WriteQrTo("https://example.com/report/1001", fileStream);
// 走框架文件系统存储:FileManager 接收流后上传(NAS / S3 同一接口)
await using var stream = new MemoryStream();
_barcodeService.WriteQrTo(content, stream);
stream.Position = 0;
var result = await _fileManager.UploadFileAsync("qr-1001.png", stream);
接口直接回图片也是流的一种去向,写法见「高级用法 → 接口直接返回条码图片」。
两个入口怎么选
| 入口 | 生成什么 | 主方法签名 |
|---|---|---|
QrCodeGenerator | 二维码 | ToPng(content, options?) |
BarcodeGenerator | 一维条形码 | ToPng(content, symbology, options?) |
两个入口的输出方法完全对称:
| 方法 | 返回 | 用途 |
|---|---|---|
ToPng | byte[] | PNG 字节,写文件、上传、嵌入 PDF |
ToBase64 | string | PNG 内容的裸 Base64,不含前缀 |
ToDataUri | string | data:image/png;base64,...,直接放 img 的 src |
WriteTo | void | 写入流(文件流、HTTP 响应流),不关闭、不刷新传入的流 |
配置项说明
QrCodeOptions(二维码)
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ErrorCorrection | ErrorCorrectionLevel | Medium | 纠错级别:Low 约 7%、Medium 约 15%、Quartile 约 25%、High 约 30% |
ModulePixels | int | 4 | 每模块像素数,不小于 2 扫码器才好识别 |
MarginModules | int | 4 | 四周留白模块数,4 是 QR 规范最小值,不建议再小 |
BarcodeOptions(一维条形码)
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ModuleWidth | int | 2 | 窄条宽度(像素) |
HeightPixels | int | 60 | 条高(像素) |
MarginPixels | int | 20 | 左右留白(像素),GS1 建议 10 倍窄条宽 |
两个 Options 的字段全部带默认值、init-only,可以直接从 JSON 反序列化后传入。
BarcodeSymbology(一维码制)
| 码制 | 内容要求 | 典型用途 |
|---|---|---|
Code128 | 全 ASCII | 物流、仓储首选 |
Code39 | 大写字母 + 数字 + 少量符号(见常见问题) | 工业传统码制 |
Ean13 | 12 位自动补校验位,或 13 位含正确校验位 | 商品零售 |
Ean8 | 7 位自动补校验位,或 8 位含正确校验位 | 商品零售短码 |
UpcA | 11 位自动补校验位,或 12 位含正确校验位 | 北美商品零售 |
Itf | 纯数字,位数必须是偶数 | 仓储、包装箱 |
Codabar | 数字 + $ - : . / + | 图书馆、血库 |
内容不满足码制要求(长度非法、字符不支持、校验位错误)时抛 BarcodeException,消息里带码制名和内容线索,好定位问题。
高级用法
接口直接返回条码图片
app.MapGet("/barcode/{code}", (string code) =>
Results.File(BarcodeGenerator.ToPng(code, BarcodeSymbology.Code128), "image/png"));
写文件(两种写法等价)
using var stream = File.Create("label.png");
BarcodeGenerator.WriteTo("Aegis-123", stream, BarcodeSymbology.Code128);
// 或者
File.WriteAllBytes("label.png", BarcodeGenerator.ToPng("Aegis-123", BarcodeSymbology.Code128));
data URI 进页面
var src = QrCodeGenerator.ToDataUri("https://example.com/report/1001");
<img src="@src" alt="报告二维码" />
Razor 页面里直接拼进 src,或者把字符串放进 JSON 回给前端。
中文与 emoji
二维码固定 UTF-8 字符集,中文、emoji 不需要额外设置:
var png = QrCodeGenerator.ToPng("德真健康-检验报告单-2026");
印制环境差时用 High 纠错
var png = QrCodeGenerator.ToPng(
"https://example.com/report/1001",
new QrCodeOptions { ErrorCorrection = ErrorCorrectionLevel.High });
热敏纸打印、标签易磨损、要在二维码中心叠图标的场景用 High,可纠错约 30%。纠错级别越高内容容量越小,屏幕展示用默认 Medium 就够。
自定义扩展
本包是静态工具类,没有继承基类或实现接口的扩展点,渲染参数由 QrCodeOptions / BarcodeOptions 覆盖。要加能力有两条路:
- 拿
ToPng返回的byte[]自行后处理(合成、嵌入 PDF) - 直接引用 ZXing.Net 在业务项目内编码更底层的格式
包内 Internal/ 下的类型不对外保证兼容,业务代码不要依赖。
接入后怎么确认生效
- 生成的字节以 PNG 文件签名开头,存成
.png文件后能被图片查看器打开 - 用手机或扫码枪扫生成的图片,读出的内容与输入一致
- 仓库单测 35 个全绿,其中有端到端用例:自产 QR 中文 PNG 用 ZXing Reader 回读文本一致,自产 Code128 PNG 回读内容一致。怀疑输出有问题时,用同样的"生成再回读"思路自验一遍
常见问题
为什么条码扫不出来
按顺序排查:ModuleWidth 是不是小于 2;一维码左右留白 MarginPixels 是不是被改小了(GS1 建议 10 倍窄条宽);Code39 内容是不是带了小写或符号(见下一问)。
Code39 内容带小写为什么没报错,扫码器却读不出来
Code39 在 ZXing 中走扩展编码(全 ASCII 转义序列),小写与符号会被编码成转义序列,生成端不报错。普通扫码器默认按标准 Code39 模式解读,读不出这些转义内容。内容只用大写字母 + 数字。
EAN-13 传 12 位还是 13 位
都行。传 12 位自动补第 13 位校验位;传 13 位时校验位必须正确,否则抛 BarcodeException。EAN-8(7/8 位)、UPC-A(11/12 位)同理。
WriteTo 之后流怎么没关
WriteTo 只写不关,打开和关闭由调用方负责。这样同一个流可以继续写别的内容,也避免误关 HTTP 响应流。
ModulePixels 能不能设很大
建议控制在 2-16。极端大值时像素缓冲区的 width * height 会触碰 int 溢出边界(回绕成负数),就算没溢出,一张几亿像素的图也会先 OOM。要大图就小幅上调并实测内存。
为什么只有黑白 PNG
输出固定 8bit 灰度 PNG(0 黑、255 白)。条码场景只需要黑白两色,PNG 编码因此不依赖任何图像库。要彩色或矢量格式,拿 byte[] 自行后处理。