PHP imagepng 输出损坏的图片问题解决

问题发现

我和同事的两套本地开发环境,相同的一段返回随机验证码图片的代码,验证码代码参考的是 php实现的Captcha验证码类实例 里的代码。在同事的电脑上调用,出来的是裂开的图片,在我的电脑上调用,出来的就是正常的图片,这是为什么呢?解决这个问题花费了三四个小时,记录一下过程。

解决过程

因为代码上没有区别,所以开始怀疑环境问题,两台开发机都是相同型号,配置也相同,PHP 运行在 docker 容器下,检查 PHP 版本相同。

查了一下 PHP 文档,怀疑可能是因为什么特性没有启用,测试以下代码:

test.php
1
2
3
4
5
<?php
if (imagetypes() & IMG_PNG) {
echo "PNG Support is enabled";
}
?>

测试结果 imagetypes() & IMG_PNG 的值大于零,看起来两个环境都是支持 PNG 图的。

比较两台电脑生成的图片,首先用 Notepad++ 打开损坏的图片,发现文件头尾都是正常的。

为了减少干扰因素,我注释掉了随机生成验证码的部分,让两个环境都生成相同的空白图片,然后用 UltraEdit 或其他 16 进制文件编辑器打开。

左为正常图片,右为损坏图片

这时候我才发现了区别,损坏图片开头多出了3个字节,EF BB BF,即我们常说的 UTF-8 BOM。

有了线索就上网查,查到了另一博客 东风无力百花残 的文章 PHP文件头BOM头问题

我刚因为一个愚蠢的问题而损失了大约4个小时。我在本地服务器上的图像以某种方式被损坏,因此没有显示在浏览器中。经过多次环顾和测试,包括多次在我的计算机上重新安装 apache,我将问题追溯到包含的文件。
没有问题不是空白,而是 UTF BOM 编码字符在我的一个未被引用的文件的开头…
所以,当心你的包含文件!
确保它们未在 UTF 中编码,或者在没有物料清单的 UTF 中进行编码。
希望它能省下别人的时间。

好的,感谢……等等,我们不太一样,我们用的是 Symfony HttpFoundation 模块做的 API,controller 也都是不包含 BOM 头的呀……

我也不是很懂 PHP,继续查,又查到了一个 StackOverflow 上的问题 Symfony2 Remove UTF-8 BOM from controller responses

好的,感谢……等等,看回复,最后他是排除了这和 Symfony 的关系,并没有给出解决方案……

这个文件头到底该怎么去掉啊……

我在本地试着给 API 的入口点 index.php 文件改成了 UTF-8 With BOM 编码,保持之后,“成功” 把图片搞裂了。

激动地去找同事检查她的 index.php,咦,并没有 BOM。

线索断了……

最后还是同事自己找到的解决方案,只要换一个思路,我们找不到 BOM 是在何处加上的,也无法保证部署后文件没有 BOM,那就在返回图片流之前,清空缓冲区。

CaptchaUtil.php
1
2
3
4
ob_end_clean();
ob_clean();
...
imagepng(...);

问题立即得到解决。

最后还是没想出来 BOM 在哪里出来的……

评论