Twentytenベースにしてカスタマイズを加えたこのサイトのテーマですが、アイキャッチ画像をヘッダーから記事へ移した事により以前にアップロードしていた画像をアイキャッチ画像にすると横長に表示される問題が発覚しました。かなり長い記事ですが、当初は「ちょっとした」問題だったのに泥沼化していく様をご覧ください。
アイキャッチ画像が横長に
Twentytenをベースにしたテーマのカスタマイズを終えて、記事内にアイキャッチ画像を無事表示できるようになりました。すでに最近の記事にはアイキャッチ画像を表示させています。
ところが、何気なく古い記事にもアイキャッチ画像を追加しようとしたところ、なぜか画像が横長に表示されてしまう現象が起こりました。
色々試してみた所、最近アップロードしたものは問題なく表示され、以前にアップロードしていた画像では横長に表示される事が分かりました。
下記は検証のため、後日ローカル環境で再現させたものです。
アイキャッチ画像はいつ作られる?
WordPressではフロント表示と管理画面の表示のため、画像をアップロードするとそれを元にサムネイル、中サイズ、大サイズの縮小画像が自動的に作成されます。
アイキャッチ画像はこれらの画像とは別の画像として専用に作成されているようです。最初はアイキャッチ画像を記事に設定した時に作成されるものと勝手に思っていましたが、テストしてみるとそうではなく、アップロードした時点で他の縮小画像と一緒に作成されていました。
なぜ横長になる?
当初は原因が分からずに悩みました。というのもアイキャッチ画像として指定する画像によっては画像サイズが変わる現象もあったためです。
ふと、Twentytenのカスタマイズを行った際に、アイキャッチ画像のデフォルトサイズ指定を行なっているコードがあったのを思い出しました。そこで、ローカル環境のWordpressでTwentytenを指定し、画像をアップロードしてテストを行ってみました。
すると、大きなサイズの画像をアップロードするとデフオルト指定通り、アイキャッチ画像のサイズが940×198ピクセルで生成されました。Twentytenではヘッダー画像の代わりにアイキャッチ画像を表示するためデフォルトサイズが940×198ピクセルに設定されているのです。
続いて、テーマをカスタマイズしたものに変更してフロントエンドで表示されているアイキャッチ画像を確認すると180×37ピクセルになっていました。
つまり、テーマとしてTwentytenを使用している時に画像をアップロードすると940×198ピクセルでアイキャッチ画像が作成されます。もし画像サイズが幅が980ピクセルに満たない場合は幅はそのままに、高さが198ピクセルになるようクロップされた画像が作成されます。例えば680×480ピクセルの画像では640×198ピクセルになります。いずれにせよ、アイキャッチ画像として横長画像が生成されます。
カスタマイズしたテーマではアイキャッチ画像を180×180ピクセルで表示しようとしていたため、940×198ピクセルの場合は180×36ピクセル、640×198ピクセルの場合は180×59ピクセルに見た目が縮小されていた訳です。
また、実際にアップロードされていたのは幅が980ピクセルに満たない画像ばかりで幅も640とか584とか、さらにスクリーンショットの場合はまちまちだったので色々なサイズの横長のアイキャッチ画像になっていました。
理屈が分かれば単純な問題なんですが、Twentytenの置土産にはまりました。参考までにTwentytenのアイキャッチ画像のデフォルトサイズの指定を行なっているコードです。functions.phpの135行目辺りにあります。
set_post_thumbnail_size( $custom_header_support[‘width’], $custom_header_support[‘height’], true );
引数として連想配列を使っていますが、それぞれ980と198がこの上の方で代入されています。また、Twentyelevenでは1000と288になっていました。
どうやって戻すか
原因の特定だけでも結構はまっていたのですが、実はここから泥沼の底に沈んでいきます。
当初メディアの編集にある「画像の編集」にサムネイルがどうのというのがあったのでそれを使えば治るかなと思っていたら、この機能ではアイキャッチ画像は生成してくれないようです。
なお、他のサイズは設定に従って作ってくれます。ただし、そのためには編集したフリをして保存する必要があり、その際にファイル名が変更されてしまいます。ファイル名が変わっても新しく作成する画像ファイルへのリンクは自動的に変わるようなのでユーザーが意識する必要は無いようですが、編集前に作成した画像のリンクは治らないと思います。
一番確実なのは画像の再アップロードする事ですが、すでに記事で使用されているためそのままでは画像がダブったりとややこしい事になりそうです。そこでネットで調べてみるとサムネイルを一括して再生成してくれるプラグインというのがちゃんとありました。
Regenerate Thumbnails
ダウンロードはWordpress公式サイトにあるRegenerate Thumbnailsのページで行えます。
縮小画像を一括再生成する
インストールは普通のプラグインと同じで、圧縮ファイルをダウンロード後解凍し、できたディレクトリをサイトのプラグインディレクトリにアップロードします。後は管理画面で有効化すればOKです。
使い方は簡単、設定も必要有りません。
プラグインが正しくインストールされていれば、管理画面のツールメニューに「Regn. thumbnails」サブメニューがあるのでクリックするとプラグインのページが開きます。後はページの下にある「Regenerate All Thumbnails」ボタンをクリックするだけ。
ボタンをクリックするとプログレスバーとログが表示されます。わかりやすいですね。
しばらく待つと変換終了です。
次なる試練
一括して再生成で楽チンと思ったのも束の間。ログの項目にfailedという文字がたくんさある・・・。あぁ、そろそろ許してください、WPの神様。
“1012-SX2311-13” (ID 638) failed to resize. The error message was: The originally uploaded image file cannot be found at /home/****/taga-works.com/public_html/wp/wp-content/uploads/1012-SX2311-13.jpg
こんな感じのロクがたくさん表示されています。(*****の部分は当方でマスクしています)
表示されたログを確認すると151枚ある画像のうち、42枚は再生成に成功していますが残る109枚についてはすべて上記のようなメッセージが表示されて失敗してしまいました。どうやら指定ディレクトリにオリジナルサイズの画像が見つからなかった模様です。
よく見るとファイルパスには本来あるはずのサブディレクトリ名が抜けており、アップロードディレクトリ直下を見に行っているようです。実際にはアップロードディレクトリ直下に年を表すディレクトリ(例えば2012等)を手動で作り、そこをアップロード先に指定していました。つまり一年分を一つのディレクトリにアップロードしていたわけです。
メディアのライブラリに戻り、問題の画像をチェックしてみるとサムネイルが表示されていません。また、サムネイルが表示されてない画像のURL欄もアップロードディレクトリ直下に画像があるような表示になっています。なお、これらの画像はライブラリを数ページ送らないと見れない位置にあるのでいつからこうなったのか、よくわかりません。
一方、フロントエンドの表示も確認すると、あら不思議、こちらは問題があった画像でもちゃんと表示されており、クリックすると元画像も開きます。どうやら、フロントエンドとライブラリで表示する仕組みが違うようです。とりあえず、大量リンク切れが発生しなくて良かった。
アップロードディレクトリの設定が変わっている
さらにややこしい事にWorspressのアップロード先の設定がいつの間にか変わっている事に気が付きました。
前述のようにこのサイトではアップロードディレクトリ直下に年を表すディレクトリを手動で作り、そこをアップロード先に指定しています。最後に設定したのは今年初めに2012のディレクトリを作成し、アップロード先もそれに合わせて変更した時です。
ところが、現在のアップロード先はデフォルトに戻されており、「アップロードしたファイルを年月ベースのフォルダに整理」にチェックが入っていました。これにより、従来はアップロードディレクトリにある「年サブディレクトリ」にアップロード先を指定になっていたのが、「年/月サブディレクトリ」に変わってしまった事になります。
今年初めて画像をアップロードしたのは8月の記事であり、それまでに間にWordpress3.4へのアップデートを行なっています。もしかすると更新する過程でディレクトリ指定がデフォルトに戻ったのかも知れません。
古い画像は再生成に成功している
コピペしてあったRegenerate Thumbnailsプラグインのログを全て確認すると不思議なことが分かりました。2011年、2010年、2009年分は失敗していますが、最近の2012年と古い2008年、2007年分は成功しています。ただし、これらの画像でも実際の記事ではちゃんと表示されています。画像ファイルそのものが消えたり、移動したわけではないので当然ですが。
アップロードした年代とアップロード先ディレクトリについて現状を整理してみると下記のようになります。
1) 2008年およびそれ以前 → 問題なし
wp-content/uploads/2008
(2007年、2008年分は別のブログで運用していたため、そこからインポートして2008サブディレクトリにまとめて保存しています)
2) 2009年〜2011年 → ファイルのURL表示がおかしい、ライブラリのサムネイルが表示されない、縮小画像の生成に失敗する
wp-content/uploads/2009
wp-content/uploads/2010
wp-content/uploads/2011
3) 2012年(8月より) → 問題なし
wp-content/uploads/2012/08
wp-content/uploads/2012/09
いずれの画像についてもフロントエンドではリンク切れは発生してないため実害は無いのですが、このままでは管理上問題です。そこで修正しようとライブラリから一枚一枚開いてURLを手で修正しようとしたら、これって修正できないのですね。まさに泣きっ面に蜂です。
直接データベースの内容を確認したら原因が判明
ライブラリから画像のURLを修正することができないため、phpMyAdminを使って直接データベースの内容を調べてみました。
アップロードされた画像の情報は投稿やページ等の情報と共にwp_postsというテーブルにアタッチメントという種類として格納されています。このテーブルの「guid」フィールドに画像URLが格納されていました。なお、「wp_」の部分はインストールの設定で変えることができるのでシステムによってはついていなかったり、別の文字になっていることもあります。
guidフィールドについて内容をチェックしてみると、予想に反して問題となっている画像でも正しいURLが格納されている事が判明しました。つまり、問題がある画像はライブラリで表示されているURLとデータベースに格納されているURLは異なっている、という事です。
謎は深まりましたが、フロントエンドでリンク切れにならないのはこのデータを見ているためだと思われます。
今度はライブラリのURLをどのように生成しているか、管理画面を作成しているPHPソースから追跡してみました。最終的にはwp-includes/post.php内にあるwp_get_attachment_url()という関数にたどり着きました。
この関数ではwp_postmetaテーブルから、「_wp_attached_file」というキーを検索し、その値を取得して画像URLを生成していました。ちなみにwp_postsテーブルのguidフィールドについてはアップロードディレクトリの情報が取得出来なかった場合にだけ利用されるようです。
以上から、今度はデータベースのwp_postmetaテーブルから_wp_attached_fileをキーにした値を見てみます。すると、問題がある画像では本来入るべきサブディレクトリ名が抜けており、ファイル名のみ格納されていることが判明しました。
例えば、2010年のtest.jpgという画像の場合は「2010/test.jpg」となるはずが、「test.jpg」とファイル名のみになっていました。最近アップロードした画像の場合は年/月サブディレクトリに入っていますが、こちらは「2012/08/test.jpg」のようにサブディレクトリも含めて保存されています。
また、後で分かったことですが、2007年〜2008年分ではフルパスになっていました。これはインポートしたためと思われます。
2009年〜2011年分はファイル名のみで2012年ではサブディレクトリを含んで記録されようになったのは内部的に仕様変更があったのか、あるいは設定オプションの「アップロードしたファイルを年月ベースのフォルダに整理」がオンになったためか、原因はよくわかりません。というか、すでに力尽きて調べる気力は失せていました。どのみち、現状ではサブディレクトリを_wp_attached_fileに追加しないとwp_get_attachment_url()関数は正しい画像URLを生成してくれないことは判明していますし。
なお、2010年9月にとったデータベースのバックアップが残っていたので、wp_postmetaテーブルから_wp_attached_fileをキーとするデータを調べてみました。その結果、上記で確認されたとおり、2007年〜2008年分についてはフルパスで、2009年〜2010年分についてはファイル名のみである事が確認出来ました。当時からアップロードした画像ファイルは_wp_attached_fileにファイル名だけが記録されていた点は間違いないなさそうです。
ファイルパスは他にも記録されていた
wp_postmetaの内容をどうやって直すか、データベースの内容を見ながら考えていたら妙なデータを見つけました。先ほどの_wp_attached_fileと共に_wp_attachment_metadataというキー名でシリアライズ化されたデータが格納されています。
シリアライズ化されているので分かりにくいのですが、その中にファイル名と思しき文字列が確認できます。
解読してみると連想配列が入っており、その内の一つの要素として「file」をキー名にして_wp_attached_fileと同じ内容が保存されている事がわかりました。
このシリアライズ化されたデータを何に使っているのか不明ですが、_wp_attached_fileだけを直すとまたトラブルの種になりそうです。ただ、シリアライズ化された状態で格納されているデータを手で直すのはかなり面倒です。(文字数がデータ構造に含まれているため、ファイル名にサブディレクトリを追加すると文字数を数えて文字数の値を修正する必要があります)
このデータの存在のおかげで、手作業による修復はほぼ絶望的となりました。プログラムを作成して実行するしか手立てはありません。でも、修正処理を実行する前に気が付いて良かった。
修復するためのプログラムを作成する
手法としては二つの方法が考えられます。
1) WordPressと独立したスクリプトを書く。
2) WordPressのプラグインとしてスクリプトを書く。
今回は以前に作ったプラグインを元にすれば比較的時間の節約になりそうなので2)のプラグイン方式としました。
修正する方法としては次のように行います。
- wp_postsのguidフィールドから画像URLを取り出し、アップロードディレクトリを基準にした相対パスを抽出。
- _wp_attached_fileから値を取り出し、1で抽出した相対パスと比較する。※1
- 合致しなければ、_wp_attached_fileに抽出したパスを上書きする。また、_wp_attachment_metadataからデータを取り出し、fileの値を抽出したパスに変更後、値を上書きする。
- すべてのアタッチメントについて1から3を繰り返す。
※1:WP上で編集を行った画像ファイルは名前が変ります。_wp_attached_fileには編集すると編集後の名前が入るようです。従って、誤検出を防ぐためにはパスからファイル名は取り除いて比較する必要があります。また、インポートしてきた場合にはフルパスが入っている事もありました。
という事で土曜日を丸一日潰して、プラグインの作成とデバッグを行いました。
いざ、実行
ようやくプラグインが完成したので、このサイトにインストールします。
他人が作ったプログラムは意識せずに実行できるのに、自分で作った場合はドキドキです。ちなみにこの「ドキドキ」は動くかなという期待感じゃなくて、データ壊さないかなという恐怖から来るドキドキです。直前にデータベースのバックアップを取っていますが、データ更新系は怖いです。
作るのは一日仕事でしたが、修正処理はあっという間に終わりました。なお、初期の画像はフルパスで有ることに気が付いたのはプラグインのデバッグを始めてからのなので、プログラムの修正は諦めてそのまま相対パスに変換されています。
変換後、メディアのライブラリを見ると・・・。
これでようやく縮小画像の再生成を実行できます。
もう一度Regenerate Thumbnailsを実行
再びRegenerate Thumbnailsプラグインを実行させると今度は全て再生成に成功しました。
以前にアップロードした画像でもアイキャッチOKに
早速、Twentyten使用中にアップロードしていてアイキャッチ画像に設定すると横長で表示された画像を再び記事のアイキャッチにしてみました。
おぉ、うまく表示されました。・・・長かった(泣。
まとめ
テーマとしてTwentytenやTwentyelevenを使用している時にアップロードした画像を別のテーマでアイキャッチ画像として表示すると、細長い横長画像で表示されます。その場合は、「Regenerate Thumbnails」プラグイン等で縮小画像一式を作り直せば現在のテーマで設定されているサイズで表示されるようになります。
途中予想外の問題が起こりましたが、とりあえず復旧できて良かった。