WordPress
AWS
S3
jq

より美しくWP Offload S3 Liteに既存のメディアを登録する方法

解決したい課題

すでに運用中のwordpressサイトがあります。コンテンツが増加し動画などのメディアファイルが多くなってきて、apacheで配信しているとサイトがどんどん重くなってしまいます。

WP Offload S3 Liteプラグインを導入して新しいメディアファイルはS3から配信されるようになりましたが、既存の大量のメディアファイルをS3から配信するにはどうすればいいのか?

という問題に遭遇したので対応方法を残しておきます。

方法2:SQLでなんとかする方法(NG)

同じようなことでお困りの方が結構いらっしゃるのでググると対応方法が出てきます。その多くはSQLでpost, post_metaを直接書き換える方法がほとんどです。
ですが、レコード内の設定内容はPHPでシリアライズされており、文字数などの記録されています。
正しく書き込むには新しい内容を正しくシリアライズしなくてはいけません。
そのためにプログラムを書かなければいけないのがデメリットです。

方法2: wp_cliでなんとかする。(better)

wb_cliはwordpressの為のコマンドラインインターフェースです。オプション豊富で様々な操作が可能です。これを使ってやってみました。

wp_contents/uploadsの内容ををAWSに転送

aws cliで一発です。

aws s3 sync wp-content/uploads/ s3://awesome-site.hoge.com/wp-content/uploads/

お決まりですが、S3バケットを作成してIAMを作成して、必要な権限だけ与えてください。
{{ bucket_name }}の部分を実際のバケット名に置き換えてください。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:CreateBucket",
                "s3:DeleteObject",
                "s3:Put*",
                "s3:Get*",
                "s3:List*"
            ],
            "Resource": [
                "arn:aws:s3:::{{ bucket_name }}",
                "arn:aws:s3:::{{ bucket_name }}/*"
            ]
        }
    ]
}

もう少し権限絞りたい気もしますが、Get/PutのみだとPluginから権限がないと怒られます。

post_metaにS3のパスを正しく記録する

プラグインを入れてからメディアファイルをアップロードすると
post_metaにS3のパスが記録されます。

そこで正しいpost_metaを書き込んであげることで、プラグインに正しくファイルの位置を教えることができます。

メディアファイルのIDを取得する

ここで処理したいのはすべてのmediaファイルです。
メディアファイルのPOST_IDをすべて取得するには、以下のコマンドを使えばいいみたいです。

メディアファイルはpost_typeがattachmentです。

./bin/wp-cli.phar post list --post_type=attachment --format=ids

これを配列にいれてLOOPで処理すれば良さそうです。

メディアファイルのパスを取得する。

postテーブルのレコードのなかでguidというフィールドにhttp://から始まるURIが書かれています。

このような形式で記録されています。

./bin/wp-cli.phar post get 6802
+-----------------------+-----------------------------------------------------------------------------+
| Field                 | Value                                                                       |
+-----------------------+-----------------------------------------------------------------------------+
| ID                    | 6802                                                                        |
| post_author           | 1                                                                           |
| post_date             | 2018-01-30 18:20:09                                                         |
| post_date_gmt         | 2018-01-30 09:20:09                                                         |
| post_content          |                                                                             |
| post_title            | 20130705170431                                                              |
| post_excerpt          |                                                                             |
| post_status           | inherit                                                                     |
| comment_status        | open                                                                        |
| ping_status           | open                                                                        |
| post_password         |                                                                             |
| post_name             | 20130705170431                                                              |
| to_ping               |                                                                             |
| pinged                |                                                                             |
| post_modified         | 2018-01-30 18:20:09                                                         |
| post_modified_gmt     | 2018-01-30 09:20:09                                                         |
| post_content_filtered |                                                                             |
| post_parent           | 0                                                                           |
| guid                  | http://awsome-site.hoge.com/wp-content/uploads/2018/01/20130705170431.png |
| menu_order            | 0                                                                           |
| post_type             | attachment                                                                  |
| post_mime_type        | image/png                                                                   |
| comment_count         | 0                                                                           |
+-----------------------+-----------------------------------------------------------------------------+

必要なのはguidの中にあるcontent/uploads/内のパスですので、正規表現を使って
http://xxx.hoge.com/ みたいなプロトコル・ドメイン名を外します。

./bin/wp-cli.phar post get $ID --field=guid | sed -e 's/\(http:.*\)\/\(wp-content.*\)/\2/'

jqを使ってpost_metaを生成する

プラグインを入れた後にメディアファイルを追加すると以下のようなレコードがpost_metaに生成されます。KEYはamazonS3_infoです。

./bin/wp-cli.phar post meta get 6802 amazonS3_info

3つの情報が記録されています。これを新たに生成してあげればよいわけです。

array (
  'bucket' => 'site.hoge.com',
  'key' => 'wp-content/uploads/2018/01/30182009/20130705170431.png',
  'region' => 'ap-northeast-1',
)

正しいJSONを生成したいので、みんなが大好きなjqを使ってゼロからJSONを構築します。
jqはAWS CLIの結果をJSONでもらって、いろんなフィルタしたりするのに使うことが多いですが、シェルスクリプトからJSONを生成するにも便利です。

実際に実行すると、このような結果が得られます。

jq -Mncr ".bucket = \"bucket-name\" | .key = \"GUID\" | .region = \"ap-northeast-1\""
{"bucket":"bucket-name","key":"GUID","region":"ap-northeast-1"}

こうして生成したJSONを変数にいれて、wp-cliを使ってpost_metaを追加します。

JSON=`jq -Mncr ".bucket = \"$BUCKET\" | .key = \"$GUID\" | .region = \"ap-northeast-1\""`
echo $JSON | ./bin/wp-cli.phar post meta add $ID amazonS3_info --format=json

ちなみにpost_metaを削除することも可能です。

./bin/wp-cli.phar post meta delete $ID amazonS3_info

出来上がり

でき当たったシェルスクリプトがこれです。
すべてのメディアファイルをWP Offload S3 LiteをつかってS3で配信することができるようになりました。

#!/bin/bash

ARRAY=(`./bin/wp-cli.phar post list --post_type=attachment --format=ids`)
for ID in "${ARRAY[@]}"; do
    GUID=`./bin/wp-cli.phar post get $ID --field=guid | sed -e 's/\(http:.*\)\/\(wp-content.*\)/\2/'`
    BUCKET='bucket-name.hoge.com'
    JSON=`jq -Mncr ".bucket = \"$BUCKET\" | .key = \"$GUID\" | .region = \"ap-northeast-1\""`
    echo $JSON | ./bin/wp-cli.phar post meta add $ID amazonS3_info --format=json
done