PHP压缩图片

Jan 3, 2015

671

前段时间,由于项目中页面速度加载太慢,需要进行优化,很重要的一个原因是页面加载图片偏多,而图片放在合作方服务器中且未经过压缩。我需要做的就是把图片从对方服务器拉取下来,存到我们公司的图片服务器上,然后对图片进行压缩。

从对方服务器上拉取下来的图片有5000多张,文件后缀名均为jpg。我采用PHP中的GD库对图片进行压缩,刚开始直接根据文件后缀名使用对应的imagecreatefromjpeg()、imagecreatefromgif()、imagecreatefrompng()生成相应的句柄。

$img = '1.jpg';
$ext = strtoupper(pathinfo($img, PATHINFO_EXTENSION));
if (is_file($img) && ($ext == 'JPG' || $ext == 'JPEG')) {
    $imgInput = @imagecreatefromjpeg($img);
} else if (is_file($img) && ($ext == 'PNG')) {
    $imgInput = @imagecreatefrompng($img);         
} else if (is_file($img) && ($ext == 'GIF')) {
    $imgInput = @imagecreatefromgif($img);
}

但是,在最终压缩完成的图片中,发现有十几张图片是压缩失败的。那究竟是为什么呢?

尝试用Photoshop打开压缩失败的图片,得到如下的提示:

原来虽然下载下来的图片后缀是jpg,实际上其中有一些图片格式并不是jpg,需要采用其他方法来识别图片的类型。

使用exif_imagetype()来判断图片类型,exif_imagetype()读取图像的第一个字节并检查其签名,通过这种识别可以减少误判断。

首先创建一个类ImageCompress,该类是一个工具类,采用单例模式,主要框图如下:

class ImageCompress
{
    private $_imgInput;//图片输入句柄
    private $_imgOutput; //图片输出句柄
    private $_imgSrc; //图片源路径
    private $_format; //图片格式
    private $_quality = 80; //图片压缩质量,默认为80
    private $_xInput; //图片原始宽度
    private $_yInput; //图片原始高度
    private $_xOutput; //图片输出宽度
    private $_yOutput; //图片输出高度
    private $_resize; //图片是否需要压缩
    private static $_handler = null; //单例模式句柄

    /**
     * 单例入口
     * @return ImageCompress
     */
    public static function getInstance()
    {
        if (is_null(self::$_handler)) {
            self::$_handler = new self();
        }
        
        return self::$_handler;
    }
    
    /**
     * 防止被复制
     */
    private function __clone()
    {
    }
    
    /**
     * 防止被外部实例
     */
    private function __construct()
    {
    }

    /**
     * 设置压缩质量
     * @param unknown $quality
     */
    public function setQuality($quality)
    {
        if (is_int($quality)) {
            $this->_quality = $quality;
        }
    }

    /**
     * 获取原始图片宽度
     * @return number
     */
    public function getWidth()
    {
        return $this->_xInput;
    }
    
    /**
     * 获取原始图片高度
     * @return number
     */
    public function getHeight()
    {
        return $this->_yInput;
    }
    
    /**
     * 清楚图片缓存
     */
    public function clearCache()
    {
        @imagedestroy($this->_imgInput);
        @imagedestroy($this->_imgOutput);
    }
    
    /**
     * 回收
     */
    public function __destruct()
    {
        $this->clearCache();
    }

    /**
     * 设置要处理的图片
     * @param string $img
     * @return boolean
     */
    public function setImg($img){}

    /**
     * 设置图片最大宽高
     * @param number $width
     * @param number $height
     */
    public function setMaxSize($width = 100, $height = 100){}

    /**
     * 设置图片最小宽高
     * @param number $width
     * @param number $height
     */
    public function setMinSize($width = 300, $height = 300){}

    /**
     * 保存图片
     * @param unknown $savedPath
     * @return boolean
     */
    public function saveImg($savedPath){}
}

setImg()方法中是根据exif_imagetype()来识别图片类型的,使用exif_imagetype()需要php启用exif支持,详见:http://php.net/manual/en/exif.installation.php

/**
 * 设置要处理的图片
 * @param string $img
 * @return boolean
 */
public function setImg($img)
{
    $this->_imgSrc = $img;
    
    if (is_file($img)) {
        switch (exif_imagetype($img)) {
            case IMAGETYPE_GIF: {
                $this->_format = 'GIF';
                $this->_imgInput = @imagecreatefromgif($img);
                break;
            }
            case IMAGETYPE_JPEG: {
                $this->_format = 'JPG';
                $this->_imgInput = @imagecreatefromjpeg($img);
                break;
            }
            case IMAGETYPE_PNG: {
                $this->_format = 'PNG';
                $this->_imgInput = @imagecreatefrompng($img);
                break;
            }
            default:{
                return false;
            }
        }
    } else {
        die($img . ' cannot be found');
    }
    
    if (empty($this->_imgInput)) {
        die($img . ' cannot be initial');
    }
    
    $this->_xInput = @imagesx($this->_imgInput);
    $this->_yInput = @imagesy($this->_imgInput);
    return true;
}

setMaxSize()方法用于设置图片的最大宽高,根据图片的宽高压缩比大小决定最终的图片宽高。

/**
 * 设置图片最大宽高
 * @param number $width
 * @param number $height
 */
public function setMaxSize($width = 100, $height = 100)
{
    if ($this->_xInput > $width || $this->_yInput > $height) {
        $resizeWidth = false;
        $resizeHeight = false;
        $ratio = 1;
        if ($this->_xInput > $width) {
            $widthRatio = $width / $this->_xInput;
            $resizeWidth = true;
        }
        
        if ($this->_yInput > $height) {
            $heightRatio = $height / $this->_yInput;
            $resizeHeight = true;
        }
            
        if ($resizeWidth) {
            $ratio = $widthRatio;
        }
        
        if ($resizeHeight) {
            $ratio = $heightRatio;
        }
        
        if ($resizeHeight && $resizeWidth) {
            if ($widthRatio < $heightRatio) {
                $ratio = $widthRatio;
            } else {
                $ratio = $heightRatio;
            }
        }
        
        $this->_xOutput = $this->_xInput * $ratio;
        $this->_yOutput = $this->_yInput * $ratio;
        
        $this->_resize = true;
    } else {
        $this->_resize = false;
    }
}

setMinSize()方法是用于设置图片的最小宽高,保证图片宽高的最小值

/**
 * 设置图片最小宽高
 * @param number $width
 * @param number $height
 */
public function setMinSize($width = 300, $height = 300)
{
    if (($this->_xInput == $width && $this->_yInput >= $height) || ($this->_xInput >= $width && $this->_yInput == $height)) {
        $this->_resize = false;
    } else {
        $ratio = 1;
        $widthRatio = $width / $this->_xInput;
        $heightRatio = $height / $this->_yInput;
            
        
        if ($widthRatio > $heightRatio) {
            $ratio = $widthRatio;
        } else {
            $ratio = $heightRatio;
        }
        
        $this->_xOutput = $this->_xInput * $ratio;
        $this->_yOutput = $this->_yInput * $ratio;
            
        $this->_resize = true;
    }
}

说到setMinSize()这个方法,就不得不提微信分享中的图标,如果页面中没有300*300以上的图片,微信会加载默认的图标,如下图所示:

如果页面中存在多个300300的图标,无论隐藏与否,微信会默认使用第一张300300作为分享中的图标。当时页面中的图片没有300300,如果直接在DOM中插入一张300300的图片,会使非微信情况下多产生一条http链接。我的解决方法:页面加载完成后,根据UA判断是否为微信,若为微信,则动态请求图片,插入到DOM中,并设置为隐藏状态。

saveImg()方法用于保存图片

/**
 * 保存图片
 * @param string $savedPath
 * @return boolean
 */
public function saveImg($savedPath)
{
    $saved = false;
    if ($this->_resize) {
        $this->_imgOutput = imagecreatetruecolor($this->_xOutput, $this->_yOutput);
        imagecopyresampled($this->_imgOutput, $this->_imgInput, 0, 0, 0, 0, $this->_xOutput, $this->_yOutput, $this->_xInput, $this->_yInput);
    }
    
    if ($this->_format == 'JPG') {
        if ($this->_resize) {
            $saved = imagejpeg($this->_imgOutput, $savedPath, $this->_quality);
        } else {
            $saved = @copy($this->_imgSrc, $savedPath);
        }
    } else if ($this->_format == 'PNG') {
        if ($this->_resize) {
            $saved = imagepng($this->_imgOutput, $savedPath, $this->_quality / 10);
        } else {
            $saved = @copy($this->_imgSrc, $savedPath);
        }
    } else if ($this->_format == 'GIF') {
        if ($this->_resize) {
            $saved = imagegif($this->_imgOutput, $savedPath);
        }
    }
    
    return $saved;
}

至此,整个ImageCompress类构建完成,用法如下:

$imgCmp = ImageCompress::getInstance();
$imgCmp->setImg("1.jpg");
$imgCmp->setMaxSize(200, 200);
$imgCmp->saveImg("2.jpg");
$imgCmp->setMinSize(300, 300);
$imgCmp->saveImg("3.jpg");
$imgCmp->clearCache();

php 」相关文章

Wen's Blog

文章归档 » 文章标签 » 博主:吴文伟,Web开发爱好者,专注于前端开发,该博客用于记录和分享平时遇到的一些问题以及知识。

订阅

联系方式

链接