【源码解读】TP5读取本地图片输出后,设置header头无效,图片乱码

简介: 在Thinkphp程序中读取本地图片,做出加工处理(如合并二维码等水印),然后输出给客户端,一直输出图片内容乱码。 设置了header image/png 不生效。 写下这篇TP源码排查文章,看看问题到底出现在哪个步骤。

在Thinkphp程序中读取本地图片,做出加工处理(如合并二维码等水印),然后输出给客户端,一直输出图片内容乱码。 设置了header image/png 不生效。 写下这篇TP源码排查文章,看看问题到底出现在哪个步骤。


乱码


3dc71e5f0bd67bfa20220a277fd8ba48.png


设置响应头无效


public function test(){
    // 请求头不生效,还是乱码
    header('Content-type: image/png');
    $file = "xxxxx\public\static\img/test.png";
    echo file_get_contents($file);
}


排查TP源码


还记得我们这篇文章吗:TP为什么可以return就输出字符串或者模板内容等等,在原生PHP不行呢? https://blog.siammm.cn/archives/168这篇文章,大概的问题还是出现在这个控制器调度类里面,我继续查看该部分源码 还是这段熟悉的源码,一样的配方,不一样的问题(bug)。


// 输出数据到客户端
if ($data instanceof Response) {
    $response = $data;
} elseif (!is_null($data)) {
    // 默认自动识别响应输出类型
    $type = $request->isAjax() ?
        Config::get('default_ajax_return') :
    Config::get('default_return_type');
    $response = Response::create($data, $type);
} else {
    $response = Response::create();
}


因为我们在控制器中没有return任何数据,这里的$data==NULL,所以会走最后一步的


$response = Response::create();


ok,那么我们看看默认的这个Response类都带了什么东东吧。


// 原始数据
protected $data;
// 当前的contentType
protected $contentType = 'text/html';
// 字符集
protected $charset = 'utf-8';
//状态
protected $code = 200;
// 输出参数
protected $options = [];
// header参数
protected $header = [];
protected $content = null;


可以看到,这里的contentType默认是text/html;


/**
 * 页面输出类型
 * @param string $contentType 输出类型
 * @param string $charset     输出编码
 * @return $this
 */
public function contentType($contentType, $charset = 'utf-8')
{
    $this->header['Content-Type'] = $contentType . '; charset=' . $charset;
    return $this;
}


在TP框架核心中,最后步骤是调用


$response->send();


这个send的代码如下,也是这个问题根源所在。


if (!headers_sent() && !empty($this->header)) {
    // 发送状态码
    http_response_code($this->code);
    // 发送头部信息
    foreach ($this->header as $name => $val) {
        if (is_null($val)) {
            header($name);
        } else {
            header($name . ':' . $val);
        }
    }
}


所以我们的header头 图片信息,被这里覆盖了


如何解决


  • 手动调用实例化response类,并且在其中设置响应头


$response = new Response();
$response->header('Content-type: image/png');
$response->data(file_get_contents($file));
return $response;


  • 我们在控制器输出图片信息后,直接die掉,不要让它走后面的框架默认事件。(虽然这样子不符合框架设计思想)
目录
相关文章
|
域名解析
超简单 图解 三级域名解析
超简单 图解 三级域名解析 最近自己在玩 搭建 ngrok 内网穿透,用到了三级域名解析,类似:blog.ngrok.xxx.com 查了下,发现这类的教程非常少,于是,我来造福你们了 下面开始 一、添加二级域名解析 主机记录填写你要使用的二级域名,我填的ngrok, 记录值填你服务器的IP地址 二、添加三级域名解析 主机记录填写 你要用的(三级域名.二级域名)我这里是泛解析,用了*.ngrok,你可以泛解析到你的二级域名,记录值填写你刚刚添加的二级域名。
13379 3
|
运维 JavaScript 应用服务中间件
怎么微信WeixinJSBridge.invoke支付成功居然不跳转?还把我页面给关了!这篇文章就告诉你What should I do!
怎么微信WeixinJSBridge.invoke支付成功居然不跳转?还把我页面给关了!这篇文章就告诉你What should I do!
1949 0
怎么微信WeixinJSBridge.invoke支付成功居然不跳转?还把我页面给关了!这篇文章就告诉你What should I do!
|
12月前
|
前端开发
如何在不使用catch方法的情况下处理Promise.reject()抛出的错误?
如何在不使用catch方法的情况下处理Promise.reject()抛出的错误?
619 155
|
12月前
|
安全 开发工具 数据库
免费源码资源源码站同城搭子系统源码多人语音聊天全套源码
对于免费源码资源,可访问GitHub、GitLab、SourceForge等开源平台,或通过开发者论坛、博客获取。同城搭子系统源码涉及社交、活动管理等功能,建议从开源社区搜索或购买商业源码。多人语音聊天源码较复杂,可在GitHub等平台搜索开源项目,或使用第三方SDK。务必注意版权、安全及技术支持。
587 1
|
弹性计算 运维 监控
OS Copilot-操作系统智能助手测试报告-新人体验
测试体验OS copilo安装、启动,体验copilot的提供的`-t`、`-f`、`|`通道的指令在在精准分析、批量任务处理、文件分析的能力。并记录下来。
402 18
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
692 15
|
算法
详尽分享算法系列:日历算法
详尽分享算法系列:日历算法
648 0
蓝易云 - 如何在Debian中同步系统时间?Debian系统时间配置(NTP服务)
以上就是在Debian系统中同步系统时间的步骤。通过以上步骤,你可以确保你的系统时间始终与NTP服务器保持同步,从而确保系统时间的准确性。
1037 3
|
JavaScript 前端开发
js怎么删除html元素
js怎么删除html元素
476 10