使用Imagick创建动态图像/ Apache标头

时间:2013-07-23 17:18:50

标签: php apache2 imagemagick imagick

在将现有的,稳定的网站转移到新服务器的过程中,我遇到了一些间歇性问题,其中包含一些使用Imagick动态创建图像的代码。

代码解析GET查询(例如example.com/image.php?ipid=750123&r=0&w=750&h=1000)然后缩放并旋转存储在服务器上的图像并将其提供给客户。

ipid = id for an image stored on server
r = degrees of rotation
w = width to display
h = height to display.

代码可能已经使用了至少5年而没有任何问题。

在转移到一个新的,更快的服务器(从Debian Squeeze到Ubuntu 12.04)时,我遇到一个问题,大约有50%的时间没有显示图像,而是服务器发送'png文件' 0个字节。没有PHP错误或服务器错误。

根据图像是否成功发送,会发送不同的标题:

成功的图片标题:

Connection: Keep-Alive
Content-Type:   image/png
Date:   Tue, 23 Jul 2013 17:03:32 GMT
Keep-Alive: timeout=5, max=76
Server: Apache/2.2.22 (Ubuntu)
Transfer-Encoding:  chunked
X-Powered-By:   PHP/5.3.10-1ubuntu3.7

图片标题失败:

Connection  Keep-Alive
Content-Length  0
Content-Type    image/png
Date    Tue, 23 Jul 2013 17:03:31 GMT
Keep-Alive  timeout=5, max=78
Server  Apache/2.2.22 (Ubuntu)
X-Powered-By    PHP/5.3.10-1ubuntu3.7

有没有人知道为什么会这样?

有没有办法'强制'将png图像发送到chunked,因为我想知道这是否是问题的根源。我尝试了各种变通方法,我通过PHP的header()函数发送图像大小或“Transfer-Encoding:chunked”作为标题,但是没有用,在这些情况下浏览器声明图像已损坏。

<?php

//Class used to connect to Imagick and do image manipulation:
class Images
{
    public $image = null;

    public function loadImage($imagePath){

        $this->image = new Imagick();
        return $this->image->readImage($imagePath);
    }

    public function getImage(){

        $this->image->setImageFormat("png8");
        $this->image->setImageDepth(5);
        $this->image->setCompressionQuality(90);
        return $this->image;
    }

    //      Resize an image by given percentage.
    //      percentage must be set as float between 0.01 and 1
    public function resizeImage ($percentage = 1, $maxWidth = false, $maxHeight = false)
    {
        if(!$this->image){return false;}
        if($percentage==1 && $maxWidth==false && $maxHeight == false){return true;}

        $width = $this->image->getImageWidth();
        $height = $this->image->getImageHeight();

        $newWidth = $width;
        $newHeight = $height;

        if($maxHeight && $maxWidth){
            if($height > $maxHeight || $width > $maxWidth){

                $scale = ($height/$maxHeight > $width/$maxWidth) ? ($height/$maxHeight) : ($width/$maxWidth) ;
                $newWidth = (int) ($width / $scale);
                $newHeight = (int) ($height / $scale);
            }
        }else{

            $newWidth = $width * $percentage;
            $newHeight = $height * $percentage;
        }
        return $this->image->resizeImage($newWidth,$newHeight,Imagick::FILTER_LANCZOS,1);

    }

    public function resizeImageByWidth ($newWidth)
    {
        if ($newWidth > 3000){
            $newWidth = 3000; //Safety measure - don't allow crazy sizes to break server.
        }

        if(!$this->image){return false;}

        return $this->image->resizeImage($newWidth,0,Imagick::FILTER_LANCZOS,1);

    }

    public function rotateImage($degrees=0)
    {
        if(!$this->image){return false;}
        return $this->image->rotateImage(new ImagickPixel(), $degrees);
    }

}


//(simplified version of) procedural code that outputs the image to browser:

$img = new Images();

$imagePath = '/some/path/returned/by/DB/image.png';

if($imagePath){
    $img->loadImage($imagePath);

    $width = $img->image->getImageWidth();
    $height = $img->image->getImageHeight();

    if (!$img->resizeImageByWidth($newWidth))
    {
        die ("image_error: resizeImage() could not create image.");
    }

    if($rotation > 0){
        if (!$img->rotateImage($rotation))
        {
            die ("image_error: rotateImage() could not create image.");
        }
    }

}else{

    die("image_error: no image path specified");
}

header('Content-type:image/png');
echo $img->getImage();

exit(0);
?>

更新:如果有助于确定问题的位置:

我已经创建了一个可行的解决方案,适用于所有情况,作为权宜之计。我所做的是创建图像,将其作为临时文件保存到磁盘。打开文件并使用passthru()将其发送到客户端,然后从磁盘中删除该文件。很麻烦,我宁愿以“整洁”的方式做,但它告诉我,这个问题与这两行有关:header('Content-type:image/png'); echo $img->getImage();以及Apache,PHP或Imagick未能处理资源。< / p>

2 个答案:

答案 0 :(得分:3)

我之前遇到过与此非常类似的问题,它与第二个请求有一个头向前的301或302状态代码有关。有些浏览器不遵循

两个图像都返回200还是失败的图像返回重定向?

答案 1 :(得分:2)

也许是一个长镜头,但也许在回声$img->getImage()之前有一些意外的输出?这会破坏输出图像。我之前遇到过一个随机的?>关闭include()标记之后的新行字符。

在搜索代码之前进行快速测试将是在输出图像数据之前使用输出缓冲来删除任何内容。

<?php
    ob_start(); //call this before executing ANY other php
?>

一段时间后......

<?php
    ob_clean(); //trash invalid data in the output buffer
    //set proper headers for image output and browser caching if desired
    echo $img->getImage();
    ob_end_flush(); //send the buffered image data to the browser
?>

当然,你确实提到了一个稳定的代码库,但是不同的web服务器或php版本可能会以不同的方式处理这个意外的空白区域。

编辑:另一个想法

新服务器是否可能正在运行某种php输出缓存机制。也许它试图从某个地方的缓存重新加载最近生成的图像,并且该部分失败,这可能是对0字节内容长度的更好解释。也许新服务器只是缺少一个库...比较每个服务器上phpinfo();的输出。