ポンコツエンジニアのごじゃっぺ開発日記。

いろいろポンコツだけど、気にするな。エンジニアの日々の開発などの記録を残していきます。 自動で収入を得られるサービスやシステムを作ることが目標!!

S3バケットのバージョニングを使って指定したバージョンのファイルを取得してみる。

AWSのS3バージョニング機能があるので、試しにいろいろ使ってみました。

f:id:ponkotsu0605:20220408223714p:plain

S3バケットのバージョニングとは

AWSのドキュメントには以下のように書いてあります。

Amazon S3 のバージョニングとは、同じバケット内でオブジェクトの複数のバリアントを保持する手段のことです。S3 のバージョニング機能を使用すると、バケットに保存されたすべてのオブジェクトのすべてのバージョンを、保存、取得、復元することができます。バージョニングを使用すれば、意図しないユーザーアクションからもアプリケーション障害からも、より簡単に復旧できます。バケットのバージョニングを有効にすると、Amazon S3 が同じオブジェクトに対する複数の書き込みリクエストを同時に受信した場合に、すべてのオブジェクトが保存されます。

バージョニングを有効にしたバケットは、オブジェクトを誤って削除したり上書きしたりしても、復元が簡単に行えます。例えば、オブジェクトを削除した場合、Amazon S3 は、オブジェクトを完全に削除する代わりに削除マーカーを挿入します。その削除マーカーが、最新のオブジェクトバージョンになります。オブジェクトを上書きすると、バケット内の新しいオブジェクトバージョンになります。いつでも以前のバージョンを復元できます。詳細については、「バージョニングが有効なバケットからのオブジェクトバージョンの削除」を参照してください。

docs.aws.amazon.com

実際に使ってみる

では、早速使ってみたいと思います。

バージョニングの有効化

S3ベケットでバージョニングを有効にする必要があります。

バケット作成時にバージョニングを有効にします。

f:id:ponkotsu0605:20220105163800p:plain

または、作成されたバケットをあとから変更することもできます。

f:id:ponkotsu0605:20220105164308p:plain

バケットのプロパティから編集ができます。

f:id:ponkotsu0605:20220105164225p:plain

docs.aws.amazon.com

アップロードする

以下のようなテキストファイルを用意してみました。

$ cat test.txt
hello world

このファイルをアップロードするためには、AWSのマネジメントコンソール上にファイルをドラッグ・アンド・ドロップするだけ。

f:id:ponkotsu0605:20220105164943p:plain

このとき、新規ファイルをアップロードしただけなので、バージョンは1つになっていることが確認できます。

f:id:ponkotsu0605:20220105165044p:plain

中身を書き換えてアップロードする

中身を書き換えました。

$ cat test.txt
World Hello!

差分はこういう感じです。

- hello world
+ World Hello!

もう一度ドラッグ・アンド・ドロップします。

f:id:ponkotsu0605:20220105165356p:plain

ファイル自体は1つに見えますが、バージョンを確認すると以下のように2つ存在することが確認できます。

f:id:ponkotsu0605:20220105165432p:plain

文字数を変えているので、ファイルサイズも変わっていることが確認できますね。

オブジェクトバージョン(VersionId)を取得する

Amazon S3コンソール上ではバージョンを確認することができましたが、実際に利用する場合は、プログラムからバージョン情報を取得できると良いですよね。

まずは、AWS CLIを利用して取得してみます。

こちらのサイトに書いてあるs3apilist-object-versionsを利用してみます。

docs.aws.amazon.com

いろいろと文字を置き換えていますが、以下のような感じで実行し、情報を取得することができました。

$ aws s3api list-object-versions --bucket my-bucket --prefix test
{
    "Versions": [
        {
            "LastModified": "2022-01-05T07:53:19.000Z",
            "VersionId": "HeAtEla5LLV2yPXtVpo4L7YPywbXdD.Q",
            "ETag": "\"XXXXXXXXXXXXXXXXX\"",
            "StorageClass": "STANDARD",
            "Key": "test.txt",
            "Owner": {
                "DisplayName": "XXXXXXXXXXXXXXXXX",
                "ID": "XXXXXXXXXXXXXXXXX"
            },
            "IsLatest": true,
            "Size": 13
        },
        {
            "LastModified": "2022-01-05T07:49:12.000Z",
            "VersionId": "pDsmB9yx7XihsrPQNy8gpXvgRzv8VEbw",
            "ETag": "\"XXXXXXXXXXXXXXXXX\"",
            "StorageClass": "STANDARD",
            "Key": "test.txt",
            "Owner": {
                "DisplayName": "XXXXXXXXXXXXXXXXX",
                "ID": "XXXXXXXXXXXXXXXXX"
            },
            "IsLatest": false,
            "Size": 12
        }
    ]
}

実行してみて思ったこと。

  • 対象ファイルのバージョンを取得する方法として、ファイルの絞り込みはprefixを使わないといけないのかな。
  • isLatesttrueのものが最新のもの

なので、もしS3に配置しているファイルのVersionIdが知りたい場合はこの方法だとよくなさそう?似たようなファイルがたくさんあったり、大量のバージョンで管理されている場合だとレスポンスが巨大になったりページネーションを利用しないといけないのでちょっとめんどくさそう。うまくprefixなどで対象ファイルを絞る必要がありそうですね。

PHPでlistObjectVersions()を用いて取得する例

PHPのソースコードだとどのように書くかも試したいと思います。

今回はaws/aws-sdk-phpを用いて操作したいと思います。

github.com

<?php

$s3_client->listObjectVersions([
    'Bucket' => 'my-bucket',
    'Prefix' => 'test',
])->get('Versions')

S3ClientクラスlistObjectVersions()を利用することで、バケットのオブジェクトを取得でき、その中のVersionsに配列で含まれます。

docs.aws.amazon.com

cliのコマンドに似た名前のメソッド名だったりレスポンスなのでわかりやすいかと思います。

オブジェクト配置時にVersionIdを取得する

配置されたものに対してVersionIdを取得する方法はなんとなくわかり、ただ用途によってはちょっと使いにくそうなことも感じました。

では、次に、S3にオブジェクトを配置するときにVersionIdを取得する方法を試したいと思います。

$ aws s3api put-object --bucket my-bucket --key test.txt --body test.txt
{
    "ETag": "\"XXXXXXXXXXXXXXXXX\"",
    "VersionId": "jWu9C3PE9k3K_ZzHXleNud4hShj87Tdn"
}

put-objectをする時のレスポンスボディにVersionIdが返ってきます。

f:id:ponkotsu0605:20220105191316p:plain

S3コンソールを確認すると、レスポンスボディに含まれるVersionIdが現行バージョンとして存在することが確認できます。

配置するときにVersionIdが取得できるので、例えば配置の度にどこかDBなどにVersionIdを記録しておけば、あとからlist-object-versionsを利用して取得する必要はなさそうですね。

docs.aws.amazon.com

PHPでputObject()したときのVersionIdの取得例

putObject()の戻り値から確認することができます。

docs.aws.amazon.com

<?php

$result = $s3_client->putObject([
    'Bucket' => 'my-bucket',
    'Key' => 'test',
    'Body' => $body,
    'ContentType' => 'text/plain; charset="UTF-8"',
]);

$resultにputした結果の情報がいろいろ含まれるのですが、その中のVersionIdという項目で格納されます。

$version_id = $result->get('VersionId');

そのため、このようにget()を呼ぶことでVersionIdが取得できます。

VersionIdを指定してS3からオブジェクトを取得

まず、現状アップロードされているのをまとめておきます。

VersionId test.txtの中身
pDsmB9yx7XihsrPQNy8gpXvgRzv8VEbw hello world
HeAtEla5LLV2yPXtVpo4L7YPywbXdD.Q World Hello!
jWu9C3PE9k3K_ZzHXleNud4hShj87Tdn(現行バージョン) World Hello!

なので、まずはそのまま取得しようとすると、現行バージョンのものが取得できます

$ curl https://url/test.txt
World Hello!

「hello world」を取得する場合は、versionIdをリクエストパラメータに追加してリクエストします。

$ curl https://url/test.txt?versionId=pDsmB9yx7XihsrPQNy8gpXvgRzv8VEbw
hello world

このとき、Access Deniedになる場合は、アクセス許可のバケットポリシー等を確認してみましょう。自分の場合は、S3のバケットをパブリックにアクセス可能にするときにGetObjectしか設定しなかったのですが、バージョンを指定して取得する場合はGetObjectVersionを指定しないといけないようです。

バージョン固有のオペレーションでは、IAM ID にバージョン固有のアクションに対するアクセス権限があることを確認します。例えば、オブジェクトの特定のバージョンをコピーするには、s3:GetObject に加えて s3:GetObjectVersion に対するアクセス権限が必要です。

aws.amazon.com

最後に

このように、S3に配置するファイルのバージョン管理ができ、各バージョンにアクセスするためのversionIdを取得することもできました。

これをうまく利用すれば、ゲームのアセットデータやマスタデータのバージョン管理の手助けになりそうですね。

お問い合わせプライバシーポリシー制作物