ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PHP生成条形码完整实践:从GD库配置到Code128编码封装

PHP生成条形码完整实践:从GD库配置到Code128编码封装 简介面向电商订单、库存标签、物流单据等场景的PHP开发者这是一份完整的条形码生成示例支持在网页中直接输出可扫描的条形码图像。压缩包约209KB共32个文件其中29个为PHP脚本覆盖条码编码、绘制、颜色与字体设置等核心逻辑另含1个TTF字体文件、条形码配置文件和说明文档目录结构一目了然。示例基于BCG类库实现内置Code 128、Code 39、EAN/UPC、ISBN等多种常见编码格式并提供可立即运行的演示页面开发者可通过修改参数调整条形码类型、尺寸和颜色也可以与数据库记录结合实现订单或商品的动态条码生成与批量验证。同时说明文档和测试脚本能帮助梳理条码数据的输入、渲染与输出流程降低上手门槛。目前已有377人学习该资源对于需要快速集成条码能力或了解条形码生成原理的开发者是一份兼具实用性与参考价值的示例。 条形码这事儿做业务系统的朋友应该都不陌生。无论是超市小票、图书编号还是快递面单背后都是一串黑白条纹在默默工作。我在好几个项目里都遇到过“生成条形码”的需求需求本身不难但要是没摸清门路光在环境准备和编码格式上就能卡半天。这次我从零写了一个 PHP 生成条形码的完整 demo把开发过程中遇到的关键点、选型思路和实际踩过的坑都整理出来希望能给正打算做类似功能的朋友一个能直接“抄作业”的参考。先交代一下背景这个 demo 的使用场景是图书管理系统的商品标签打印需要根据数据库里存储的图书编号实时生成对应条形码并在前端页面上展示、支持下载。开发环境是老牌的 LNMP 架构PHP 版本 7.4服务器是宝塔面板底层依赖 GD 库处理图像。整体需求不算复杂但麻雀虽小五脏俱全从类库选型、环境配置、代码封装到前端联调每个环节都有值得说道的细节。1. 需求分析与实现方案选型1.1 demo 要解决的核心问题在拿到需求的第一时间我先做了拆解。表面上看需求就是“根据编号生成一张条形码图片”但往深处想至少有四个问题需要提前回答第一条形码图片的格式是什么直接输出 PNG 流给前端还是把图片文件保存到服务器这个决定了后面的接口设计和存储方案。第二条形码的码制选哪一种Code128、Code39、EAN-13 各有各的适用场景。图书编号这种场景通常用 Code128因为它支持全部 ASCII 字符编码密度高同样的内容长度下生成的图片更短打印机出错的概率也低。第三生成效率如何如果只是偶尔生成一张随便怎么实现都行但如果要批量打印库存里几千条数据就必须考虑循环生成的耗时和资源占用。第四图片质量能否满足打印要求屏幕上看不清楚的条纹打印出来往往是灾难。条形码的宽度、高度、边距quiet zone都需要可配置。带着这四个问题我进入选型阶段。1.2 主流 PHP 条形码方案对比PHP 生态里生成条形码的方案不算少我梳理了三个主流方向各有优劣。第一种是用纯 PHP 类库直接生成图片典型代表是 Picqer 出品的 php-barcode-generator。这类方案不依赖系统层面的扩展通过 GD 库逐像素绘制条形码安装简单、兼容性好支持 Code39、Code128、EAN-13 等常见码制。优点是部署方便composer 一条命令搞定缺点是涉及复杂二维码比如 QR Code时力不从心只能专注条形码。对这个 demo 来说它是首选。第二种是使用图形库扩展比如 Zend\Barcode 组件现在归到 Laminas 名下。它的优点是代码组织规范和框架结合紧密提供了一整套渲染和验证机制缺点是依赖较重如果项目本身不用 Laminas 全家桶单拉一个组件进来多少有点“杀鸡用牛刀”。第三种是调用第三方 HTTP API 生成条形码。这种方式的好处是完全不占用本地资源前端把编号传给接口接口返回图片 URL 就行。但缺点也很明显每次生成都是一次网络往返内网环境或离线环境直接抓瞎而且数据安全是个潜在大坑商品编号、图书编号这类业务数据经过第三方服务等于把自己的家底交给别人保管。综合评估我选了 Picqer 的 php-barcode-generator。理由很直白依赖最少、在 GD 库基础上工作、对 PHP 版本要求友好5.6 以上都能跑、社区使用面广、遇到问题能快速搜到解决方案。这个选择后来被验证是正确的整个开发过程几乎没有被类库本身的 bug 卡住过。2. 环境准备与依赖安装2.1 运行环境检查在动手写代码之前先确认服务器环境是否满足要求。这个步骤看起来多余但真有人装完依赖才发现 GD 库缺失白折腾半小时。我用下面这段命令快速检查 PHP 版本和已启用的扩展php -v php -m | grep gd正常输出里应该能看到gd字样。如果没看到说明 GD 扩展没有启用。在宝塔面板里操作很简单打开“软件商店”-“PHP 7.4”-“设置”-“安装扩展”找到 GD 扩展点击安装即可。命令行环境的朋友则需要根据系统包管理器安装比如 Ubuntu 下的apt-get install php7.4-gd装完记得重启 PHP-FPM。另外还有一种情况需要注意就是 GD 库虽然安装了但可能缺少 FreeType 字体支持。这个特性主要用于在图片上绘制文字如果我们后续需要在条形码下方显示可读的编号文字就离不开 FreeType。检查命令是php -r var_dump(gd_info());输出数组里如果有FreeType Support bool(true)就说明没问题。2.2 安装 php-barcode-generator 类库环境就绪之后用 Composer 安装类库就是一句命令的事composer require picqer/php-barcode-generator安装完成后vendor 目录里会出现picqer/php-barcode-generator。如果你用的是 ThinkPHP、Laravel 这类框架Composer 自动加载机制会把命名空间注册好直接在业务代码里use Picqer\Barcode\BarcodeGeneratorPNG;就能用。没有框架的原生 PHP 项目需要手动引入vendor/autoload.php。这一步几乎不会遇到问题唯一可能出现的情况是 PHP 版本过低导致 Composer 版本不兼容但只要是 PHP 5.6 以上基本都稳。2.3 存储目录与权限预设类库安装只是第一步真正到业务层面还需要考虑图片存哪里。我在 demo 里规划了一个/public/barcodes目录专门存放生成好的条形码图片并保证 Web 服务器有写入权限。为什么单独建目录而不是直接放在临时目录因为业务场景要求条形码可以重复展示图书编号“9787121418174”对应的图片每次打开详情页都应该一致重复生成是浪费资源。把生成结果缓存到指定目录下次访问时先判断文件是否存在存在就直接返回这样页面响应速度能快上一个数量级同时也方便后续做打印批量导出。权限设置上目录所有者设为运行 PHP 的用户宝塔环境下通常是www权限值设为755或775均可。千万别图省事直接777会有安全隐患。3. 核心代码实现与开发实录3.1 基础版三行代码生成条形码先把最基本的调用方式展示出来。使用BarcodeGeneratorPNG类只需要指定内容、码制和宽高因子就能生成一张 PNG 格式的条形码图片?php require vendor/autoload.php; use Picqer\Barcode\BarcodeGeneratorPNG; $generator new BarcodeGeneratorPNG(); $barcode $generator-getBarcode(9787121418174, $generator::TYPE_CODE_128, 2, 50); file_put_contents(public/barcodes/9787121418174.png, $barcode);这段代码做的事情很直观调用getBarcode()方法传入三个参数其中2是像素放大倍数50是条形码高度单位也是像素。生成的二进制字符串通过file_put_contents写入文件就是一张可直接访问的 PNG 图片。3.2 参数深度解析为什么这样调优getBarcode()方法的参数看着简单实际调优空间很大四个核心参数的理解直接决定了成图质量。第一个参数是条形码内容。这里要注意Code128 支持的数字和字母范围很广但如果我们用 EAN-13 码制内容必须恰好是 12 位或 13 位数字多一位少一位都会抛异常。业务系统里商品编号往往有多种格式所以推荐统一使用 Code128兼容性最好。第二个参数是码制常量。php-barcode-generator 提供了TYPE_CODE_39、TYPE_CODE_128、TYPE_EAN_13、TYPE_UPC_A等多种选择。码制的差异直接影响条形码能否被扫码枪识别这一点非常关键。我见过有同事生成了一堆 Code39 的图片结果仓库扫码枪根本认不出来排查半天才发现是码制的问题。对于现代扫码设备首选 Code128几乎不会出兼容性问题。第三个参数是宽度因子Width Factor。这个参数控制条纹的基本宽度。值越小条形码整体越窄但过小会导致扫描器难以识别值越大条形码越宽占用的标签面积也就越大。经验值在 2 到 3 之间比较合适。我用 2 做屏幕预览打印时改用 3。第四个参数是高度Height。条形码高度至少要保证在 30 像素以上太矮的条形码扫起来很费劲尤其是手持扫码枪对不齐角度的时候。我习惯设置 50如果标签纸空间紧张也会压到 40但低于这个值扫码成功率会有肉眼可见的下降。3.3 进阶生成带文字说明的条形码很多业务场景要求在条形码下方显示对应的编号文字方便人工核对。PNG 生成器本身支持在图片底部附加文字设置方法如下?php $generator new BarcodeGeneratorPNG(); $generator-setColor(0, 0, 0); // 条形码颜色默认黑色 $barcode $generator-getBarcode($code, $generator::TYPE_CODE_128, 2, 50);但getBarcode()生成的图片默认不包含文字。如果需要文字可以在生成后调用 GD 函数自行绘制或者使用更高阶的 SVG 生成器配合 CSS 控制文字。考虑到 demo 要展示完整能力我封装了一个函数合并“生成条形码”和“绘制文字”两个步骤?php require vendor/autoload.php; use Picqer\Barcode\BarcodeGeneratorPNG; /** * 生成带文字的条形码图片 * * param string $code 条形码内容 * param int $width 宽度因子 * param int $height 条形码高度 * return string 二进制PNG图片数据 */ function generateBarcodeWithText(string $code, int $width 2, int $height 50): string { $generator new BarcodeGeneratorPNG(); $barcodeImageData $generator-getBarcode($code, $generator::TYPE_CODE_128, $width, $height); $image imagecreatefromstring($barcodeImageData); $imageWidth imagesx($image); $imageHeight imagesy($image); // 底部预留20像素高度用于显示文字 $newHeight $imageHeight 20; $newImage imagecreatetruecolor($imageWidth, $newHeight); // 填充白色背景 $white imagecolorallocate($newImage, 255, 255, 255); $black imagecolorallocate($newImage, 0, 0, 0); imagefill($newImage, 0, 0, $white); // 将原条形码拷贝到新画布的顶部 imagecopy($newImage, $image, 0, 0, 0, 0, $imageWidth, $imageHeight); // 在底部中央绘制文字 $fontPath __DIR__ . /assets/arial.ttf; $fontSize 12; $textBox imagettfbbox($fontSize, 0, $fontPath, $code); $textWidth $textBox[2] - $textBox[0]; $textX intval(($imageWidth - $textWidth) / 2); imagettftext($newImage, $fontSize, 0, $textX, $imageHeight 16, $black, $fontPath, $code); ob_start(); imagepng($newImage); $data ob_get_clean(); imagedestroy($image); imagedestroy($newImage); return $data; }这里用到了几个 GD 库的基础函数简单说一下意图先读取原条形码图片的宽高然后新建一个比原来高 20 像素的画布白色背景打底把原条形码原样拷贝到顶部再用imagettftext()在底部居中的位置绘制编号文字。字体文件我用的是arial.ttf放到了项目的 assets 目录下你也可以换成自己喜欢的开源字体但注意字体文件路径要写对否则imagettfbbox()会直接抛异常。3.4 完整 demo从数据库读取编号并生成条形码既然场景是图书管理系统那 demo 就必须有数据库交互。我建了一张简单的图书表包含id、book_name、isbn三个字段。核心逻辑是接收前端传入的图书 ID从数据库查出对应 ISBN调用上面封装好的函数生成条形码图片最后返回给前端。?php require vendor/autoload.php; use Picqer\Barcode\BarcodeGeneratorPNG; $pdo new PDO(mysql:host127.0.0.1;dbnamelibrary, root, your_password); $id intval($_GET[id] ?? 0); if ($id 0) { http_response_code(400); exit(invalid id); } $stmt $pdo-prepare(SELECT isbn FROM books WHERE id ?); $stmt-execute([$id]); $book $stmt-fetch(PDO::FETCH_ASSOC); if (!$book) { http_response_code(404); exit(book not found); } $isbn trim($book[isbn]); $barcodeFile __DIR__ . /public/barcodes/ . $isbn . .png; // 文件存在则直接返回避免重复生成 if (!file_exists($barcodeFile)) { $imageData generateBarcodeWithText($isbn); file_put_contents($barcodeFile, $imageData); } header(Content-Type: image/png); readfile($barcodeFile);这段代码里有几个细节值得多说一句。第一我用PDO预处理语句防止 SQL 注入虽然这只是一个 demo但这种安全习惯应当从一开始就养成。第二用intval($_GET[id])强制类型转换把非法参数挡在门外。第三文件缓存逻辑让同一 ISBN 的条形码只生成一次减少 I/O 开销。4. 前端展示与下载集成4.1 页面展示方案后端接口已经能够输出 PNG 图片前端展示就很简单了。最直接的方式是把接口地址放在img标签的src属性里img src/barcode.php?id1 alt条形码到这一步用户访问商品详情页就能看到条形码。但我还做了一点小增强提供一张独立的展示页用户输入编号即可预览条形码方便测试人员校验生成效果。这个页面用了一个简单的 HTML 表单!DOCTYPE html html langzh-CN head meta charsetUTF-8 title条形码生成Demo/title style body { font-family: sans-serif; max-width: 800px; margin: 40px auto; padding: 0 20px; } input[typetext] { width: 300px; padding: 8px; font-size: 16px; } button { padding: 8px 24px; font-size: 16px; cursor: pointer; } .result { margin-top: 30px; padding: 20px; border: 1px solid #eee; text-align: center; } /style /head body h2条形码生成Demo/h2 form onsubmitevent.preventDefault(); loadBarcode(); input typetext idcode placeholder请输入编号如 9787121418174 button typesubmit生成条形码/button /form div classresult img idbarcodeImg alt条形码预览 /div script function loadBarcode() { const code document.getElementById(code).value.trim(); if (!code) { alert(编号不能为空); return; } document.getElementById(barcodeImg).src /barcode.php?code encodeURIComponent(code); } /script /body /html这里要注意因为我在 JS 里用了encodeURIComponent()对参数进行编码后端接收时也应该用$_GET[code]获取并做基本校验避免特殊字符破坏 URL 结构。4.2 点击下载与打印适配展示只是第一步用户最终要把条形码贴到商品上所以下载和打印是刚需。我在展示页里加了两个按钮一个触发下载一个触发打印。下载功能实现思路很简单加一个 HTTP 响应头Content-Disposition: attachment即可。我新增了一个download.php文件几乎和barcode.php逻辑一致只是响应头不同header(Content-Type: image/png); header(Content-Disposition: attachment; filename . $isbn . .png); readfile($barcodeFile);打印功能则利用了浏览器的能力把条形码图片放进去调用window.print()。这里值得注意的一点是打印样式中需要把页面多余的边距、按钮、输入框隐藏掉只留下条形码区域否则打印出来会浪费一整张纸。我在 CSS 里加了媒体查询media print { body * { visibility: hidden; } #printArea, #printArea * { visibility: visible; } #printArea { position: absolute; left: 0; top: 0; } }这套“隐藏所有元素再显示目标区域”的做法是前端打印的经典套路用起来非常稳。5. 常见问题与排查技巧实录5.1 高频问题速查表开发过程中我整理了一份高频问题清单按出现概率排序方便遇到相似情况快速定位。问题现象可能原因解决方案生成图片为空白或纯色块服务器的 GD 库未启用或版本过低检查php -m安装或升级 GD 扩展调用getBarcode()抛出“错误码长度”异常使用 EAN-13 等定长码制但内容位数不符改用 Code128 码制或补齐内容位数生成的条形码扫码无法识别宽度因子太小或图片被拉伸变形宽度因子至少设为 2前端展示不要随意拉伸图片图片下方文字乱码字体文件路径错误或字体不支持中文检查 TTF 字体路径改用支持中文的字体如思源黑体大量生成时内存爆掉循环里创建的图像资源未释放每次生成后调用imagedestroy()释放资源本地正常但服务器上不显示图片未安装 PHP 的 fileinfo 扩展或图片目录无写权限安装 fileinfo 扩展检查目录权限5.2 独家避坑心得第一个坑是字体路径。几乎所有用过imagettftext()的开发者都会被字体路径坑过。绝对路径虽然一劳永逸但代码一旦迁移到别的服务器就得改。我的习惯是把字体文件放进项目目录用__DIR__拼出完整路径这样项目打包到哪里都不会丢字体。第二个坑是文件锁。如果有多个人同时首次访问某个条码可能出现并发请求同时检测到文件不存在、同时生成图片的情况。虽然业务上影响不大只是多生成几次但在高并发场景下会增加服务器压力。解决办法是使用文件锁$lockFile $barcodeFile . .lock; $fp fopen($lockFile, w); flock($fp, LOCK_EX); if (!file_exists($barcodeFile)) { file_put_contents($barcodeFile, generateBarcodeWithText($isbn)); } flock($fp, LOCK_UN); fclose($fp);第三个坑是打印清晰度。如果发现打印出的条形码扫描不通过优先检查图片的分辨率而不是怀疑生成逻辑。浏览器显示时如果页面 CSS 把图片缩放得太小或者打印选项里设置了“适应页面”导致图片被压缩都会让条纹糊成一团。建议在打印样式里给条形码图片设置固定宽度比如width: 200px;保证原始像素不被过度缩小。第四个坑比较隐蔽和 PHP 的内存限制有关。默认memory_limit是 128M生成几十张条形码图片通常没事但如果你在循环里把每一张图片都用imagecreatefromstring()解析一遍再拼到一张大图上批量打印内存很容易飙到 200M。遇到这种情况要么在循环里实时释放资源要么临时调大内存限制ini_set(memory_limit, 256M);我个人更推荐前者代码更稳健。6. 从 demo 到生产环境的扩展思路demo 跑通只是第一步真要接到生产环境还有几件事值得提前考虑。第一引入 Redis 或文件缓存机制。条形码内容通常是数据库里的标识字段短时间内不会变化。我的 demo 里已经做了文件缓存但如果系统是分布式部署多台服务器共享文件目录还要考虑文件同步问题。更稳妥的方案是把图片转成 Base64 字符串存入 Redis设置一个合理的过期时间前端通过接口直接取 Base64 data URI 展示彻底绕开静态文件分发。第二考虑接入条形码打印中间件。如果你所在的公司用的是斑马Zebra这类工业条码打印机服务器端生成 PNG 图片只是万里长征第一步。真正的生产链路往往是后端生成条码数据 - 传给打印服务 - 打印服务控制打印机。好在 php-barcode-generator 支持输出 SVG 格式SVG 矢量图在打印时不会失真非常适合这个方向。use Picqer\Barcode\BarcodeGeneratorSVG; $generator new BarcodeGeneratorSVG(); $svg $generator-getBarcode($isbn, $generator::TYPE_CODE_128, 2, 50);第三统一封装成 service 类。如果你是一个喜欢代码整洁的人强烈建议把条形码生成、缓存、验证封装成一个独立的服务类和控制器解耦。这样换类库、换存储方案都只需要改一处不用全局搜索散落的file_put_contents。我在实际项目中用的就是类似下面这个简化的服务接口interface BarcodeServiceInterface { public function generate(string $code): string; // 返回PNG的Base64编码 public function getPath(string $code): string; // 返回图片缓存路径 public function exists(string $code): bool; // 判断缓存是否存在 }一开始觉得麻烦但后来需求从“生成一张图书条码”扩展到“生成各种物料编码条码”的时候这套抽象帮我省了大力气。最后再分享一个小技巧调试阶段可以把生成结果直接输出成 Base64 字符串在浏览器地址栏粘贴查看省去不断写文件、删文件的麻烦header(Content-Type: text/plain); $data (new BarcodeGeneratorPNG())-getBarcode(demo123, BarcodeGeneratorPNG::TYPE_CODE_128); echo base64_encode($data);把输出贴到浏览器里前面加上data:image/png;base64,前缀就能直接预览。这个法子特别适合排查“图片能不能生成”和“生成内容对不对”这两个基本问题。条形码这功能吧看着不起眼但真到扫码枪怼上的时候出了问题比程序报错还让人头大。希望这份实测记录能帮你少走几个我当时走过的弯路。本文还有配套的精品资源点击获取
返回列表