使わない縮小サイズの画像を作らない:medium_large など 3 つを止め、今あるファイルも片付ける

広告WordPress・PHP を学べる本

Amazonのアソシエイトとして、WordPress functions.phpは適格販売により収入を得ています。

管理しているサイトで、サーバーのファイルを見直していたときのことです。

その中の 1 つで、画像のフォルダのファイルが多いことに気づきました。

調べると、画像を 1 枚アップロードするたびに、どこでも使っていない縮小サイズの画像も作られていました。

今回検証したサイトでは、medium_large(768px)・1536x1536・2048x2048 の 3 つを使っていないことを確認しました。

この記事では、この 3 つを作らないようにするフィルターと、すでにあるファイルの片付け方、作業環境で確かめた結果をまとめます。

WordPress が作る縮小サイズ

WordPress は、画像をアップロードすると、元の画像のほかに、決まった大きさの画像を作ります。

管理画面の「設定」→「メディア」に出るのは、サムネイル・中サイズ・大サイズの 3 つだけです。

ほかに、画面に出ないサイズも作られています。

名前 大きさ(初めの値) メモ
thumbnail 150×150(切り抜き) 「設定」→「メディア」で変えられる
medium 300×300 まで 「設定」→「メディア」で変えられる
medium_large 幅 768 まで メディアの設定の画面には出ない
large 1024×1024 まで 「設定」→「メディア」で変えられる
1536x1536 1536×1536 まで WordPress 5.3 から。画面には出ない
2048x2048 2048×2048 まで WordPress 5.3 から。画面には出ない
テーマ・プラグインのサイズ それぞれ add_image_size() で足したもの

このサイトのテーマも、add_image_size() で独自のサイズを 4 つ足していました。

そのため、1 枚の画像から、たくさんのファイルができていました。

どのサイズが登録されているかを見る

まず、登録されているサイズを出して、テーマのどこで使っているかを見比べました。

登録されている縮小サイズを出す
<?php
// 登録されている縮小サイズを、名前と大きさで出す(検証用スクリプトの一部)
foreach ( wp_get_registered_image_subsizes() as $name => $size ) {
    echo $name, ' ', $size['width'], 'x', $size['height'], ( $size['crop'] ? ' crop' : '' ), "\n";
}
書き方 役目
wp_get_registered_image_subsizes() 登録されている縮小サイズを、名前・幅・高さ・切り抜くかで返す(WordPress 5.3 から)
$size['crop'] 決まった大きさに切り抜くサイズなら true

次に、テーマのフォルダを、サイズの名前で grep しました。

本文の画像は large で、テーマの部品は thumbnail と独自のサイズで出していました。

medium_large・1536x1536・2048x2048 は、どこでも名前を指定して使っていませんでした。

1. 新しく作らない:intermediate_image_sizes_advanced

WordPress は、縮小サイズを作る前に、作るサイズの一覧を intermediate_image_sizes_advanced というフィルターに通します。

ここで一覧から外したサイズは、作られません。

functions.php(またはテーマから読むファイル)
<?php
// 使わない縮小サイズ(medium_large・1536x1536・2048x2048)を作らない
// 本文の画像は large、テーマは thumbnail と独自のサイズを使っている。この 3 つはどこでも使っていない。
function my_skip_image_sizes( $sizes ) {
    unset( $sizes['medium_large'], $sizes['1536x1536'], $sizes['2048x2048'] );
    return $sizes;
}
add_filter( 'intermediate_image_sizes_advanced', 'my_skip_image_sizes' );
書き方 役目
intermediate_image_sizes_advanced アップロードのときに作る縮小サイズの一覧を、作る前に変えられるフィルター
$sizes サイズの名前をキーにした配列(幅・高さ・切り抜くか)
unset( … ) 作らないサイズを一覧から外す。テーマで使うサイズは外さない
return $sizes; 残ったサイズだけが作られる

このフィルターが効くのは、これからアップロードする画像だけです。

すでにあるファイルは、そのまま残ります。

2. 今あるファイルを片付ける:WP-CLI のスクリプト

すでにある縮小サイズのファイルは、フィルターと同じ日に、スクリプトで片付けました。

ファイルを消すだけでは足りません。

画像のメタデータ(_wp_attachment_metadata)に、サイズの名前とファイル名が残っています。

残っていると、srcset などで、もうないファイルの URL が出てしまいます。

そのため、ファイルを消すのと一緒に、メタデータからもそのサイズを外します。

/wp-content/themes/(テーマのフォルダ)/tools/remove-image-sizes.php
<?php
// 使わない縮小サイズ(medium_large・1536x1536・2048x2048)のファイルを消し、画像のメタデータからも外す。
// 元の画像と、ほかのサイズには触らない。WP-CLI から動かしたときだけ動く。
//
// 数えるだけ: wp eval-file wp-content/themes/my-theme/tools/remove-image-sizes.php
// 本当に消す: MY_DO=1 wp eval-file wp-content/themes/my-theme/tools/remove-image-sizes.php
if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
    exit;
}

$do      = ( '1' === getenv( 'MY_DO' ) );
$targets = array( 'medium_large', '1536x1536', '2048x2048' );
$upload  = wp_get_upload_dir();
$webpc   = WP_CONTENT_DIR . '/uploads-webpc/uploads'; // Converter for Media の WebP の置き場所

$ids = get_posts(
    array(
        'post_type'      => 'attachment',
        'post_mime_type' => 'image',
        'post_status'    => 'inherit',
        'posts_per_page' => -1,
        'fields'         => 'ids',
    )
);

$count = array( 'images' => 0, 'files' => 0, 'webp' => 0, 'missing' => 0, 'shared' => 0 );

foreach ( $ids as $id ) {
    $meta = wp_get_attachment_metadata( $id );
    if ( empty( $meta['sizes'] ) || ! is_array( $meta['sizes'] ) ) {
        continue;
    }
    $original = get_attached_file( $id );
    if ( ! $original ) {
        continue;
    }
    $dir = dirname( $original );

    // 残すサイズが使っているファイル名(同じファイルをほかのサイズが使っていたら消さない)
    $keep = array( basename( $original ) => true );
    foreach ( $meta['sizes'] as $name => $size ) {
        if ( ! in_array( $name, $targets, true ) && ! empty( $size['file'] ) ) {
            $keep[ $size['file'] ] = true;
        }
    }

    $hit = false;
    foreach ( $targets as $name ) {
        if ( empty( $meta['sizes'][ $name ]['file'] ) ) {
            continue;
        }
        $hit  = true;
        $file = $meta['sizes'][ $name ]['file'];
        unset( $meta['sizes'][ $name ] );
        if ( isset( $keep[ $file ] ) ) {
            $count['shared']++;
            continue;
        }
        $path = $dir . '/' . $file;
        if ( is_file( $path ) ) {
            $count['files']++;
            if ( $do ) {
                unlink( $path );
            }
        } else {
            $count['missing']++;
        }
        // WebP のコピー(uploads-webpc/uploads/年/月/ファイル名.webp)
        $relative = ltrim( str_replace( wp_normalize_path( $upload['basedir'] ), '', wp_normalize_path( $path ) ), '/' );
        $webp     = $webpc . '/' . $relative . '.webp';
        if ( is_file( $webp ) ) {
            $count['webp']++;
            if ( $do ) {
                unlink( $webp );
            }
        }
    }

    if ( $hit ) {
        $count['images']++;
        if ( $do ) {
            wp_update_attachment_metadata( $id, $meta );
        }
    }
}

WP_CLI::log(
    sprintf(
        '%s: images %d / files %d / webp %d / missing %d / shared %d',
        $do ? 'deleted' : 'dry-run',
        $count['images'],
        $count['files'],
        $count['webp'],
        $count['missing'],
        $count['shared']
    )
);
書き方 役目
if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) exit; WP-CLI から動かしたときだけ動く。ブラウザで URL を開かれても何もしない
getenv( 'MY_DO' ) MY_DO=1 のときだけ消す。付けなければ数えるだけ
get_posts( … 'post_type' => 'attachment' … ) メディアの画像を、ID だけ全部取る
wp_get_attachment_metadata() 画像のメタデータ。sizes にサイズごとのファイル名が入っている
get_attached_file() 画像のファイルの場所。縮小サイズのファイルは同じフォルダにある
$keep 残すサイズが使っているファイル名。同じファイルをほかのサイズも使っていたら消さない
unlink() ファイルを消す
uploads-webpc Converter for Media(WebP に変えるプラグイン)が作る WebP のコピー。使っていなければ見つからないだけ
wp_update_attachment_metadata() サイズを外したメタデータを保存する
WP_CLI::log() 数えた結果を出す

元の画像と、残すサイズのファイルには触りません。

動かす順

SSH でサーバーに入って
# サイトのフォルダ(wp-config.php がある所)で
# 1. 数えるだけ(消さない)
wp eval-file wp-content/themes/my-theme/tools/remove-image-sizes.php

# 2. 本当に消す
MY_DO=1 wp eval-file wp-content/themes/my-theme/tools/remove-image-sizes.php
順 すること
1 アップロードのフォルダと DB のバックアップを取る
2 フィルターを入れたテーマを公開する(新しいファイルが増えないようにする)
3 数えるだけで動かし、消すファイルの数と、「見つからなかった」「ほかのサイズと同じファイル」の数を見る
4 MY_DO=1 を付けて動かし、本当に消す
5 記事のページを開き、画像が出ているかを見る

エックスサーバーでは、SSH で入ると wp コマンド(WP-CLI)が使えます。

このサイトの本番は PHP 7.3 だったので、スクリプトもその書き方に合わせました。

確かめた結果

このサイトの作業環境(本番から複製したもの・WordPress 7.1.3・PHP 8.3)で確かめました。

検証用スクリプトで、作業環境の WordPress を読み込み、3000×2000 の JPEG の画像を作ってアップロードの処理(wp_generate_attachment_metadata())に通しました。

フィルターがあるとき・ないとき

試したこと メタデータのサイズ できたファイル
テーマのフィルターがあるとき thumbnail・medium・large と、テーマの 4 つ 8 個(元の画像・-scaled・縮小 6 個)
フィルターを外したとき(比べるため) 上に加えて medium_large・1536×1536・2048×2048 11 個(768×512・1536×1024・2048×1365 が増えた)

フィルターがあるときは、3 つのサイズのファイルが作られず、メタデータにも入りませんでした。

縮小が 7 サイズあるのにファイルが 6 個なのは、thumbnail とテーマのサイズの 1 つが同じ 150×150 で、1 つのファイルを一緒に使っていたためです。

どちらのときも、-scaled の付いた画像ができていました(下の「気をつけること」)。

片付けのスクリプト

作業環境の画像を全部消すわけにはいかないので、フィルターを外して入れた試しの画像 1 枚だけに、スクリプトを動かしました。

検証用スクリプトの中で WP_CLI の定数と WP_CLI::log() を用意し、pre_get_posts で、対象をその 1 枚にしぼりました。

試したこと 結果
数えるだけ 消す対象のファイル 3 個。ファイルは 11 個のまま(消していない)
MY_DO=1 で消す 3 個を消した。ファイルは 8 個になり、元の画像と -scaled は残った
消したあとのメタデータ medium_large・1536×1536・2048×2048 がなくなり、ほかのサイズは残った
消したあとの srcset(large) 1024w と 300w だけ。消したファイルの URL は出なかった
作業環境の画像全体で、数えるだけ 消す対象が見つかった。「見つからなかった」「ほかのサイズと同じファイル」は 0(何も消していない)

本番の PHP 7.3 でも、php -l で書き方のエラーがないことを確かめました。

元に戻した

試しの画像は、確かめたあとに wp_delete_attachment() で消しました。

投稿の表とメタデータの表の行の数、アップロードのフォルダのファイルの数が、試す前と同じであることを確かめました。

気をつけること

  • テーマやプラグインが add_image_size() で足したサイズは、外さないでください。外すと、そのサイズで出している所が、元の画像や別のサイズで出るようになります。先にサイズの名前で grep して、使っていないものだけを外します
  • -scaled の画像は、縮小サイズとは別の仕組みです。幅か高さが 2560px より大きい画像は、WordPress 5.3 から、2560px に縮めた -scaled の画像が作られ、それがふだん使う画像になります。2048x2048 を外しても、-scaled は作られます(作業環境で確かめました)
  • -scaled を作らないようにするには、big_image_size_threshold のフィルターを使います(下のコード)。ただ、そうすると大きな元の画像がそのまま使われるので、このサイトでは止めていません(試していません)
  • 片付けのスクリプトは、-scaled の画像と元の画像(メタデータの original_image)には触りません。sizes の中の 3 つのサイズだけを見ています
  • 消したサイズは、srcset の候補から外れます。大きな画面で large より大きい画像を出したいテーマでは、外してよいか先に確かめてください
  • あとでサムネイルを作り直すプラグインを使うときも、フィルターが入っていれば、3 つのサイズは作られないはずです(試していません)
  • 消す前に、アップロードのフォルダと DB のバックアップを取ってください
-scaled を作らないとき(参考)
<?php
// 大きな画像を 2560px に縮める動き(-scaled)を止める(このサイトでは使っていません・試していません)
add_filter( 'big_image_size_threshold', '__return_false' );

まとめ

  • WordPress は、設定の画面に出ない medium_large・1536x1536・2048x2048 も、アップロードのたびに作っている
  • wp_get_registered_image_subsizes() でサイズを出し、テーマで使っていないものを確かめる
  • intermediate_image_sizes_advanced で外したサイズは、新しいアップロードで作られない
  • すでにあるファイルは、消すのと一緒にメタデータからも外す。WP-CLI の wp eval-file で、数えるだけ → 消す、の順に動かす
  • テーマのサイズと -scaled の画像は、別に考える

参考

関連機能

  1. WordPress の自動更新を止める:更新のお知らせは消さずに、手で安全に更新する設定

  2. WordPress dashicons.min.cssファイルの読み込みをログイン時だけにする ( WordPress公式アイコン )

  3. WordPress REST API を無効化 ( 管理者ログイン時、Contact Form 7 プラグイン利用時は有効 )

サイドバー

よく使うカスタマイズ

最近の記事

Profile

PAGE TOP