AWSの資格勉強(SAAなど)をしていると、必ず出てくる定番の知識があります。
「S3はオブジェクトストレージであり、EFSのようなファイルストレージとは異なり、ディレクトリ階層構造を持たない」
私も丸暗記して、「またこれかー、サービス問題やわ」とドヤ顔していました。
しかし、いざ実務でPythonスクリプトを書いていた時、この知識の意味を本当には理解していなかったことを、エラーログに叩きつけられることになりました。
今日は、私がS3の「幻のディレクトリ」に躓き、IndexError で時間を溶かした話を共有します。
1. そもそも「S3にはディレクトリがない」とは?
私たちが普段PCで扱うファイルシステム(やEFS)は、本物の「木構造」をしています。フォルダの中にフォルダがあり、その中にファイルがあります。
しかし、S3は「バケット」という巨大なプールの中に、ファイル(オブジェクト)がフラットに浮いているだけです。
AWSコンソールを見ると、綺麗にフォルダ分けされているように見えますよね?
my-bucket/
└ logs/
└ 2025/
└ 10/
└ app.log
でも、S3から見れば、これは「logsフォルダの中の2025フォルダの中の...」ではなく、 「logs/2025/10/app.log というめちゃくちゃ長い名前(キー)がついた1個のファイル」 でしかありません。スラッシュ(/)はただの文字です。
ここまでは、教科書通りの知識です。
2. コンソール機能が生む「ディレクトリオブジェクト」の罠
ここからが実務の落とし穴です。
S3にはディレクトリがないはずなのに、AWSコンソールには親切にも 「フォルダの作成」というボタンがあります。
ディレクトリがないのにフォルダを作る とは?
実はこれ、キーの末尾が / で終わる、中身が空っぽの0バイトオブジェクト を作成しているんです。
これを便宜上「ディレクトリオブジェクト」と呼びます。
ここで認識のズレが起きる
-
SDK/CLIでアップロードした場合:
prefix/file.txtというキーでアップロードすれば、間のフォルダオブジェクトは作られない -
コンソールでポチポチ作った場合:
prefix/という実体のない0バイトオブジェクトが生成されてしまう
厄介なことにこれら、コンソール上での見た目は完全に同じなのです。
S3のコンソールは、人間が扱いやすいよう徹底的に「ファイルシステムっぽく」振る舞おうとするらしく、ありがた迷惑。
真実を知るには、CLIでlsを叩くしかないです。
この「目に見えない0バイトの幽霊」が、後のコード実行時に牙を剥きます。
3. 事件発生:探索スクリプトが落ちた
私はS3上のログファイルを探索し、ファイル名から正規表現でIPアドレスなどを抽出するPythonスクリプトを書いていました。
# イメージ:特定の日付プレフィックス以下の全オブジェクトを取得
prefix = "logs/2025/11/04/"
objects = bucket.objects.filter(Prefix=prefix)
for item in objects:
# ファイル名から必要な情報を抽出(ここで事故る)
# 'logs/2025/11/04/ip-10-xxx.gz' みたいなのを期待している
match = re.findall(r'ip-[0-9]+', item.key)[0]
print(match)
テスト環境で実行したところ、突然の IndexError: list index out of range。
「えっ、ログファイルなら絶対このパターンを含んでいるはずなのに、なんでマッチしないの?」
原因は「フォルダそのもの」だった
デバッグして判明したのは、bucket.objects.filter が返したリストの中に、ログファイルだけでなく、
logs/2025/11/04/ という「フォルダ(ディレクトリオブジェクト)」そのもの
が含まれていたことでした。
- S3「ヘイ、
prefixに一致するオブジェクトを持ってきたぞ」 - コード「ありがとう。1つ目は...
logs/2025/11/04/app.logだな。正規表現マッチ!」 - コード「2つ目は...
logs/2025/11/04/だな。正規表現マッチ... しない! リストが空だ![0]にアクセスできない! 死!」
「S3はオブジェクトストレージ(=フォルダなんてない)」と頭では分かっていたのに、コードを書くときは「返ってくるのはファイルだけでしょ」というファイルストレージ的な感覚が抜けきっていなかったわけです。
4. 教訓と対策
コードでの回避策
S3をプログラムで走査するときは、「フォルダオブジェクトが混ざっているかもしれない」と常に疑う必要があります。
一番簡単な対策は、末尾の / をチェックすることです。
for item in bucket.objects.filter(Prefix=prefix):
# ディレクトリオブジェクト(フォルダ)ならスキップ!
if item.key.endswith('/'):
continue
# 以降、ファイルに対する処理
# ...
この2行を入れるだけで、コンソールで作られたフォルダも、そうでない場合も安全に処理できます。
運用のベストプラクティス
そして根本的な対策として、「S3ではむやみにフォルダを作らない(ディレクトリを掘らない)」ことが重要です。
-
aaa/bbb/ccc/ddd.xmlを置きたいなら、コンソールでaaa作ってbbb作って...とやる必要はありません。 - アップロード時にファイルのキー(名前)を
aaa/bbb/ccc/ddd.xmlに指定すれば、勝手にその階層にあるように見せてくれます。
未経験文系新卒エンジニアとしてはどうしてもCLIよりもGUIのほうが扱いやすい気がして、目の前の「フォルダの作成」に飛びついてしまったのでした。
(普通に考えると、GUIでポチポチするよりもCLIでザーッと一括登録する方が楽です)
まとめ
- 知識: S3にはディレクトリはない。あるのは「キー」だけ。
-
現実: コンソールの「フォルダ作成」は、
/で終わる空オブジェクトを作っている。 - ハマりポイント: プログラムで一覧取得すると、ファイルに混じって「フォルダオブジェクト」が返ってきて、処理を破壊することがある。
「S3はオブジェクトストレージ」。
SAAの勉強中はただの暗記ワードでしたが、エラーと格闘したおかげで、ようやくこの言葉の本当の意味が五臓六腑に染み渡りました。
みなさんが何かの拍子でこの記事を思い出してエラーを回避してくれたらうれしいです。