最近服务器上的图片越来越多了,并且前期并没有对用户上传的图片进行压缩处理,导致服务器硬盘有点吃紧了。心疼钱钱嘛,于是琢磨着怎么将他们压缩一下,减少图片体积,腾点空间出来。重要的是,还能节省不少带宽。

说干就干,先是到搜索引擎搜搜看别人是怎么处理的,结果搜了一圈,没找到合适的方案,大部分文章都是抄来抄去,参考意义不大。

于是到GitHub上翻翻,看看有没有现成的直接可用项目。由于目前的项目是php写的,所以理所当然的想着现成的php方案。结果翻了一圈没找到,倒是找到了tinypng/tinyjpg。不过这些都是调用的别人的api,不是我想要的结果。

最终功夫不负有心人,终于分别给jpg、png、gif格式的图片找到了压缩的方法。

实际上,仅仅依靠php是搞不定的。我们需要分别安装针对不同图片格式的压缩工具。

gif图片的压缩工具是gifsicle,jpg图片的压缩工具是jpegoptim,png的图片压缩工具是pngquant。因为我的项目只允许使用这三种图片,所以,这里我只研究这三种图片的压缩方式。

我的服务器是centos服务器,因此,这里只介绍在centos服务器上如何安装和使用图片压缩工具。

安装 gifsicle jpegoptim pngquant

yum install gifsicle jpegoptim pngquant

至此,需要安装的软件安装好了,我们再编写php代码,来调用他们实现压缩功能:

<?php
/**
 * 这是一个图片压缩类文件
 * 支持gif、jpg、png压缩
 * @ author www.kandaoni.com
 */

class cls_compress
{
    public $file;
    public $new_file;
    public $infos;
    public $cmd;
    public $errors;

    protected $force = true;
    protected $quality = null;
    protected $quality_max = null;
    protected $progressive = false;
    protected $extension;

    private static $extensionAlias = array(
        'jpeg' => 'jpg'
    );

    public function __construct($file, $new_file = null)
    {
        $this->file = $file;
        if(!$new_file) {
            $this->new_file = $this->file;
        } else {
            $this->new_file = $new_file;
        }
        if($this->new_file != $this->file) {
            if(copy($this->file, $this->new_file)) {
                @chmod($this->new_file, 0777);
            } else {
                $this->setError('Can not copy : ' . $this->file);
            }
        }
        $this->extension = self::getExtension($file);
    }

    public static function getExtension($file)
    {
        $info      = new SplFileInfo($file);
        $extension = $info->getExtension();

        return array_key_exists($extension, self::$extensionAlias) ? self::$extensionAlias[$extension] : $extension;
    }

    public function exec()
    {
        $this->getCommand();

        if ($this->cmd) {
            exec($this->cmd,  $info);
        }
        $this->getInfos();
    }

    public static function getFormatSize($bytes, $decimals = 1)
    {
        $size   = array('b', 'ko', 'Mo');
        $factor = floor((strlen($bytes) - 1) / 3);

        return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$size[(int)$factor];
    }

    /**
     * 根据不同的图片格式,添加不同的命令。
     */
    public function getCommand()
    {
        if($this->extension == 'gif') {
          $this->addCommand('gifsicle ' . $this->new_file . ' -o ' . $this->new_file . ' -O3');
        } else if($this->extension == 'jpg') {
          $this->addCommand('jpegoptim ' . $this->new_file . ' --strip-all -o  --m ' . $this->quality);
        } else if($this->extension == 'png') {
          $this->addCommand(
            'pngquant ' . $this->new_file .' -o ' . $this->new_file.' --force'.
            ($this->quality && $this->quality_max ? ' --quality=' . $this->quality . '-' . $this->quality_max : '')
          );
        }
    }

    public function setQuality($min, $max = null)
    {
        if ($min >= 0 && $min <= 100) {
            $this->quality = $min;
        }
        if ($max && $max >= 0 && $max <= 100 && $max > $this->quality) {
            $this->quality_max = $max;
        }
        return $this;
    }

    public function setSize($w, $h = null, $crop = false)
    {
        if (!$h) {
            $h = $w;
        }

        $size = getimagesize($this->file);
        if ($w > $size[0] && $h > $size[1]) {
            return false;
        }

        $cmd = 'convert ' . $this->new_file . ' -resize ' . $w . 'x' . $h;
        if ($crop) {
            $cmd .= '^ -gravity center -extent ' . $w . 'x' . $h;
        }
        $cmd .= ' ' . $this->new_file;
        $this->addCommand($cmd);
        return $this;
    }

    protected function addCommand($cmd)
    {
        $this->cmd .= $cmd . ';' . "\n";
    }

    public function getInfos()
    {
        if (!empty($this->errors)) {
            return array('errors' => $this->errors);
        } else {
            $original_size = filesize($this->file);
            $new_size      = filesize($this->new_file);
            $saved_size    = $original_size - $new_size;
            $percent_saved = 100 - ((100 * $new_size) / $original_size);
            return array(
                'original_size' => self::getFormatSize($original_size),
                'new_size'      => self::getFormatSize($new_size),
                'saved_size'    => self::getFormatSize($saved_size),
                'saved_percent' => round($percent_saved, 2),
                'file'          => basename($this->file),
                'new_file' => basename($this->new_file)
            );
        }
    }

    public function setError($error)
    {
        $this->errors[] = $error;
    }
}

上面的处理类中,允许传入原图和新图片。如果没有传入新图片地址,则会覆盖原图。再根据不同的图片格式,调用不同的命令。最终执行结果,可以通过调用getInfos获得,如果有错误,getInfos会返回错误信息,否则就返回压缩处理后的压缩率等信息。

调用方法:

$imageFile = "/data/wwwroot/www/test/test.png";
$compress = new cls_compress($imageFile);
$compress->setQuality(85)->exec();
print_r($compress->getInfos());

上面的代码,会将test.png图片按质量85%来压缩图片,并打印质量信息。实际上服务器运行不需要打印,只需要执行完就可以了。执行完了后,压缩后的图片,会替换掉原图。如果需要压缩后的图片另外存储,则可以传入第二个参数:

$imageFile = "/data/wwwroot/www/test/test.png";
$destFile = "/data/wwwroot/www/test/new_test.png";
$compress = new cls_compress($imageFile, $destFile);
$compress->setQuality(85)->exec();
print_r($compress->getInfos());

这样子,压缩后的图片就会保存成new_test.png了。

上面的图片压缩类,可以单独调用,也可以