PHP使用Base64解码打印PDF时输出原始字符串而非正常PDF问题
问题原因
- 9100端口是网络打印机的RAW打印端口,默认仅接收打印机原生支持的页面描述语言(如PCL、PostScript、标签机的ZPL等),直接发送PDF二进制流时,只有自带PDF直解模块的高端商用打印机能识别渲染,绝大多数普通打印机会把收到的二进制数据当纯文本输出,就会出现打印PDF源码、乱码的现象。
- 代码中调用
printer('', 9100, $base64)时打印机IP参数传空,开发阶段连接本地/共享打印机时这段socket逻辑本身就无法正常工作。 - 写PDF文件时用了
w文本模式,在Windows等系统下会自动转义换行符,可能导致生成的PDF文件本身损坏。 - 用
exec("rm -rf")删除临时文件存在命令注入风险,且没有必要调用系统命令删文件。
修复方案
根据你的使用场景选以下任意一种方案即可:
方案1:调用系统打印服务(兼容性最高,支持所有打印机)
废弃直接socket发流的逻辑,把生成的PDF交给操作系统的打印队列处理,由系统完成PDF渲染和打印机适配,这是最稳定的通用方案。
不同系统的实现逻辑:
- Linux(CUPS打印服务):用
lp命令提交打印任务 - Windows:可以调用系统
print命令,或借助SumatraPDF等轻量工具实现静默打印
替换原有socket发送部分的代码示例:
// 写入文件时改成wb二进制模式,避免PDF损坏 $pdf = fopen($docname, 'wb'); fwrite($pdf, $pdf_decoded); fclose($pdf); // Linux CUPS打印示例,$printer参数传CUPS中配置的打印机名称 $escapedDoc = escapeshellarg($docname); $escapedPrinter = escapeshellarg($printer); exec("lp -d {$escapedPrinter} {$escapedDoc}", $output, $returnVar); if ($returnVar === 0) { echo "Successfully printed"; unlink($docname); // 用PHP原生函数删文件,避免命令注入 } else { echo "Print failed, error code: " . $returnVar; unlink($docname); }
注意:运行PHP的进程用户(如www-data、apache)需要加入打印用户组,拥有CUPS操作权限。
方案2:必须走9100网络端口打印的场景
如果不能依赖系统打印服务,必须直连网络打印机的9100端口,需要先把PDF转换成对应打印机支持的页面描述语言再发送:
- 服务器安装Ghostscript工具
- 生成临时PDF后,调用Ghostscript将PDF转换为打印机兼容的PCL/PostScript格式,转换命令示例(转PostScript):
gs -dNOPAUSE -dBATCH -sDEVICE=ps2write -sOutputFile=print_data.ps 你的临时PDF路径
如果目标打印机支持PCL,可将sDEVICE参数替换为对应PCL版本的驱动(如ljet4对应PCL5e)
3. 读取转换后的print_data.ps文件内容,通过socket发送到打印机9100端口即可。
方案3:打印机支持PDF直打场景
如果使用的是明确标注支持「PDF Direct Print」功能的高端商用打印机,不需要转格式,但需要在发送PDF流前,先发送对应厂商规定的控制指令,将打印机切换到PDF解析模式,具体指令需要查阅对应打印机型号的开发手册,普通消费级打印机无此功能,无需尝试。
额外优化建议
- 用
fsockopen代替pfsockopen,打印是一次性任务,不需要持久化连接,避免连接残留。 - 所有文件写入操作如果是二进制内容,都用
wb模式打开,避免跨平台兼容问题。 - 临时文件删除统一用PHP原生
unlink()函数,不要拼接shell命令执行,防范命令注入风险。
内容的提问来源于stack exchange,提问作者Joossieπ
相关产品推荐
相关产品推荐

