Amazon S3のバケットにあるファイルから最新のファイル名を1つ取得する

  • AWS
  • S3
  • Kotlin

この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。

背景

S3バケットに置いてあるファイルの中から、一番最近アップロードされたファイル名を1つだけ取りたかった。ListObjectsV2で一覧を取り、lastModifiedでソートして先頭を取れば済む。

Kotlinでの実装

fun getLatestUploadObject(bucketName: String, objectPrefix: String): S3ObjectSummary? {
val request = ListObjectsV2Request().also {
it.bucketName = bucketName
it.prefix = objectPrefix
}
val s3 = AmazonS3Client(credentialsProvider, Region.getRegion(Regions.AP_NORTHEAST_1))
val result = s3.listObjectsV2(request)
return result.objectSummaries.maxByOrNull { it.lastModified }
}

注: このコード自体はKotlinから呼んでいるが、AmazonS3Client / ListObjectsV2Request / S3ObjectSummary は実体としてはAWS SDK for Java 1.x(com.amazonaws.services.s3.*)のクラス群。公式のAWS SDK for Kotlin(aws.sdk.kotlin.services.s3.*、S3Clientはsuspend funベース)とは別物なので、SDKの選定時は混同しないほうがいい。1000件を超える場合のページングAPIも両者で名前・使い方が異なる。

CLIの場合

AWS CLIで同じことをやるなら、JMESPathのsort_byとreverseでlastModified降順に並べ替えて先頭を取る。

ターミナル
aws s3api list-objects-v2 \
--bucket "<BucketName>" \
--query 'reverse(sort_by(Contents, &LastModified))[:1].Key' \
--output text

注意点:1000件を超える場合

ListObjectsV2(KotlinのSDK呼び出し・AWS CLIのlist-objects-v2いずれも)は、MaxKeysを指定しない場合デフォルトで最大1000件しか返さない。加えて返ってくる順序はlastModifiedではなく、キー名のUTF-8バイナリ順(辞書順)。汎用バケットの場合、ListObjectsV2はキー名の辞書順でオブジェクトを返す仕様になっている。

つまり対象プレフィックス配下に1000件を超えるオブジェクトがある場合、1回のリクエストで取れた1000件の中にそもそも最新のオブジェクトが含まれていない可能性がある。取得後にlastModifiedでソートしても、母集団に最新ファイルが入っていなければ意味がない。

1000件以内であることが分かっている場合や、キー名にタイムスタンプなど昇順になるプレフィックスを付けている場合は問題にならない。そうでない場合は継続トークンで全件ページングするか、最新ファイルの参照だけを別オブジェクトとして持つような設計を検討した方がよさそう。