qrcode.Encode 返回 image.RGBA 像素数据,需经 png.Encode 编码为 PNG 字节流才能正确显示;加 Logo 必须手动 draw.Draw;中文等内容需 URL 编码;高并发下应缓存 PNG 字节流而非 image.RGBA。

qrcode.Encode 返回的是 *image.RGBA,不是 PNG 文件
很多人调用 qrcode.Encode 后直接写文件或返回 HTTP 响应,结果打开是损坏图或浏览器报错。根本原因是它返回的是原始像素数据(*image.RGBA),必须经 png.Encode 编码才能变成浏览器能识别的 PNG 字节流。
常见错误现象:
- 文件大小只有几十字节,用
file qrcode.png查看显示 “data” 而非 “PNG image” - HTTP 响应头设了
Content-Type: image/png,但浏览器提示“无法加载图像”
正确做法是:先 qrcode.Encode 得到 *image.RGBA,再用 png.Encode 写入 io.Writer(如文件、http.ResponseWriter 或 bytes.Buffer)。
加 Logo 必须手动 draw.Draw,qrcode 库本身不支持
go-qrcode 没有 WithLogo 或类似选项 —— 所有 Logo 合成都得自己操作像素。核心流程是:生成二维码图 → 加载并缩放 Logo → 计算居中偏移 → 用 draw.Draw 贴到中心区域。
立即学习“go语言免费学习笔记(深入)”;
关键细节:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- Logo 尺寸建议缩放到二维码模块数的 1/4~1/5(例如 256px 二维码,Logo 控制在 40–60px),太大容易盖住左上、右上、左下三个定位方块
- 必须用
golang.org/x/image/draw,别用imaging或gift等重型库,它们引入额外依赖且对简单贴图无优势 - 叠加时要用
draw.Over模式,并确保 Logo 图片带 alpha 通道(PNG 格式),否则背景会变黑 - 坐标计算别硬写
128, 128,要用qrImg.Bounds()和logo.Bounds()动态算:image.Pt((w-lw)/2, (h-lh)/2)
中文内容要 URL 编码,否则旧扫码器可能扫不出
直接把中文字符串(如 "你好世界")传给 qrcode.Encode,生成的二维码在微信旧版、部分安卓扫码 App 上大概率识别失败 —— 不是库的问题,是 QR 标准对 UTF-8 的兼容性差异。
稳妥方案是用 url.QueryEscape:
qrcode.Encode(url.QueryEscape("你好世界"), qrcode.Medium, 256)- 如果内容本身就是 URL(如
"https://example.com/路径?name=张三"),也必须整体编码,避免&、=被解析器截断 - 全角标点(如“。”、“,”)和不可见字符(如 Word 复制来的零宽空格)同样会导致识别率下降,建议输入前做
strings.TrimSpace+ 正则清理
高并发场景下必须缓存 []byte,别每次重新 Encode
qrcode.Encode 是纯 CPU 密集型操作,无状态但耗时稳定(256px 约 0.5–2ms)。QPS 过千时,CPU 很容易打满,而问题不在算法,而在重复计算。
缓存策略建议:
- key 用
text + size + level拼接(如"https://a.co/123|256|M"),注意统一编码格式(URL 编码后存) - value 存
[]byte(PNG 字节流),不是*image.RGBA—— 后者无法安全并发读,且序列化开销大 - 用
sync.Map或带 TTL 的内存缓存(如 freecache),避免 map 并发写 panic - 边距(
margin)、颜色等定制项一旦加入,就得进 key,否则缓存会错乱
真正难的不是贴 Logo,而是让 Logo 刚好不碰那三个角上的大方块 —— 坐标差 1 像素、缩放比例多 5%,都可能导致扫码器拒识。动手前先拿一张标准二维码打印出来,用尺子量下定位块实际尺寸,比调十次代码更管用。

















