メモ wordpressPHP

大量の大ファイルサイズ画像をPHPでリサイズ・圧縮をする方法

2021.08.15

こんにちはjunです。私はこの技術ブログ以外にもブログを運営しているのですが、そちらはwebエンジニアになる前からやっていました。しかし過去の記事を見ているとやたらと重い画像、1枚あたり1MBも使っている記事が何枚もありました。当時は画像の重さなど知らずにデジカメの画像をそのままアップロードして、使用していたことが原因です。

記事の中には転送量が12MBにもなっているものもあり、原因は画像でした。それらの画像はやたらとサイズ(寸法)が大きいことが原因なので、リサイズして圧縮することで解決できます。しかし100件以上の記事を1つずつ開いて調べたり、もう一回ダウンロードして、圧縮してアップロードなんて何日かかるかわかりません。そのため今回はPHPとコマンドラインを用いて一気に画像を処理することにしました。

wordpressを使用していますが、画像処理のエッセンスや大方の処理の流れは応用できるとおもいます。では早速やっていきましょう。

大まかな流れ

まず全体の処理の流れですが以下の通りです。

  1. ターミナルで wp-content/uploads/ 配下にある大サイズ(500kb以上とする)をリストアップ。
  2. バックアップしておき、数ヶ月残しておく。
  3. PHPのGDモジュールを用いてリサイズ・圧縮を行う。
  4. 処理した画像を上書き。

以上の通りです。今回の処理を行うにあたりwordpressがあるサーバーに対してCLI操作が行える必要があります。SSHができ、必要な権限を付与しておいてください。今回はこの準備はできているものとします。

処理対象をリストアップ

それではまず対象の画像をリストアップします。アップロードディレクトリに移動してfindコマンドで簡単に見つけられます。

# /documentroot/wordpress/ は仮です。あなたの環境に合わせてください。
# 画像が格納されているuploadsへ移動
cd '/documentroot/wordpress/wp-content/uploads'
ls
2017 2018 2019 2020 2021 aaaa bbbbb cccc

# プラグイン用画像のディレクトリなどもあるので注意!
# 自分がアップロードした画像があるディレクトリのみを指定、500kb以上のものをリストアップ。リストはテキストファイルとしてホームディレクトリにおいておく。
find 2017 2018 2019 2020 2021 -size +500k > ~/list.txt
..
2017/12/d7d6b39a47612239ce48bf6579be7f83-644x279.png
2017/12/d7d6b39a47612239ce48bf6579be7f83-720x340.png
2017/12/d7d6b39a47612239ce48bf6579be7f83-768x332.png
2017/12/d7d6b39a47612239ce48bf6579be7f83.png
2017/12/DSC03961-1200x900.jpg
2017/12/DSC04310-1200x900.jpg
2017/12/DSC04311-1200x900.jpg
2017/12/DSC04314-1200x900.jpg
2017/12/DSC04315-1200x900.jpg
2017/12/DSC04316-1200x900.jpg
2017/12/DSC04317-1200x900.jpg
2017/12/DSC04319-1200x900.jpg
2017/12/DSC04320-e1512559527929-1200x1600.jpg
2017/12/DSC04324-1200x900.jpg
2017/12/DSC04325-1200x900.jpg
...

find -size でファイルサイズでフィルターできます。1000件以上あったりと、コピーするのは大変なので list.txt などで出力しておきます。

画像処理スクリプトを作成

リストをPHP Arrayにする

サーバーでのリストアップが終わったので、ローカルでスクリプトを作っていきます。list.txt はローカルに移動しておきます。

.
├── list.php
├── image.php
├── list.txt

list.txtlist.php としてコピーしておき、配列に変換しておきます。テキストエディタの置換機能などを利用してください。

list.php
<?php 
$list =[
"2017/06/23c7f697593e4a4c83e01310ee4b84ec-1200x900.jpg",
"2017/06/23c7f697593e4a4c83e01310ee4b84ec.jpg",
"2017/06/cropped-DSC03989-1-1024x723.jpg",
"2017/06/cropped-DSC03989-1-1200x847.jpg",
"2017/06/cropped-DSC03989-1.jpg",
...
];

これで対象画像をリストアップして、PHPが扱えるようになったので image.php にて処理の方を書いていきましょう。

画像圧縮・リサイズ処理

そのまえに..

PHPでリサイズなどの画像処理を行う場合はGDというPHPモジュールを用います。処理を書く前にこのGDモジュールが使用しているPHP環境にあるかを確認しましょう。

phpinfoのページでも確認できますが、手っ取り早くCLIでやりましょう。以下のコマンドを打ちます。

php --info | grep GD
GD Support => enabled
GD headers Version => 2.3.2
GD library Version => 2.3.2

GD Support => enabled が存在し、enabledとなっていれば使用可能です。ない場合などを別途インストールが必要です。まあwordpressが使用できる環境ならば大抵入っていると思います。これでGDの確認を行ったら、処理を書きます。

処理の記述

今回のスクリプトはCMSがるサーバー上で実行するものとします。そのためパスなどの構成も予め、サーバー上であることを想定してます。

全体は以下の通りです。

<?php 
    require_once 'list.php';

    $uploadRoot = '/documentroot/wordpress/wp-content/uploads/';

    foreach($list as $file){
        /** 
         * ファイル名などを分けておく。
         * 
         * 例
         * $file            => '2017/12/DSC04325-1200x900.jpg'
         * $targetFilename  => 'DSC04325-1200x900.jpg'
         * $fullPath        => '/documentroot/wordpress/wp-content/uploads/2017/12/DSC04325-1200x900.jpg'
        */
        $filenames = explode('/',$file);
        $targetFilename = $filenames[count($filenames)-1];
        $fullPath = $uploadRoot.$file;

        // getimagesize()を用いて寸法、拡張子の情報を取得。
        list($w, $h,$type) = getimagesize($fullPath);
        var_dump($fullPath);

        // imagecreatefromjpeg()・imagecreatefrompng()にてGDImageを取得。画像を取得しているんだなぐらいだと思ってください。
        // JPG/PNGによって分ける必要あり!。
        switch ($type) {
            case IMAGETYPE_JPEG:
                $original_image = imagecreatefromjpeg($fullPath);
                break;
            case IMAGETYPE_PNG:
                $original_image = imagecreatefrompng($fullPath);
                break;
            default:
                var_dump('対応していないファイル形式です。: '.$fullPath);
                continue;
        }

        // 寸法が大きすぎるもの(1000px以上)は600pxぐにらいするリサイズ処理を行う。
        if($w >= 1000){

            // imagecreatetruecolor() で新しい画像を埋め込むための「枠」を作る。(新しいキャンバス的な?)
            // 縦横比を計算させて元画像と同じにさせること。
            $newW = 600;
            $newH = $newW * ($h / $w);
            $newImg = imagecreatetruecolor($newW, $newH);

            // リサイズ処理。
            $success = imagecopyresampled($newImg, $original_image, 0, 0, 0, 0, $newW, $newH, $w, $h);

            if($success){
                $original_image = $newImg;
            }else{
                var_dump('リサイズ失敗: '.$fullPath);
                continue;
            }
        }

        // 画像を圧縮して同じ名前、パスで上書き保存する。ここも拡張子で異なるので注意!
        // 数字は圧縮の品質。低いほど軽くなるが、粗くなる。jpgは0~100,pngは0~9なので注意。
        switch ($type) {
            case IMAGETYPE_JPEG:
                imagejpeg($original_image,$fullPath,60);
                break;
            case IMAGETYPE_PNG:
                imagepng($original_image,$fullPath,6);
                break;
            default:
                var_dump('対応していないファイル形式です。: '.$fullPath);
                continue;
        }
    }
?>

書いてあるコメントの通りです。ただGDは拡張子ごとに使用する関数を分ける必要があるので、そこがめんどいです。listで予めどんな拡張子が使用されているかを確かめておきましょう。ローカルでテストを行ったら、image.php, list.phpをサーバーに転送します。

実行前の注意!

実行する前に必ず uploads のバックアップをしておきましょう。そうすればもしやらかしたり、予想以上に粗くなったとしてもやり直しができます。OKであれば

php image.php

そして実行しましょう。私の場合1500画像で5分ほどで終わった気がします。

ls -lh や上記のfindコマンドで重い画像がなくなったかを確認しましょう。また、画像のパスをブラウザで叩いて確認してもいいかもしれません。キャッシュが効いていることが多いので、シークレットモードで見ることをお勧めします。

Copyright © 2021 jun. All rights reserved.