【AWS S3】 .gz ファイルのブラウザダウンロード時に起こる「拡張子と中身の不一致」問題
はじめに
AWS S3 からファイルをダウンロードする際、ブラウザを使用すると予期せぬ動作に遭遇することがあります。特に GZIP 圧縮ファイルをダウンロードした際、拡張子は .gz のままなのに中身が解凍されてしまうという現象が発生します。この記事では、なぜこのような現象が起きるのか、そしてどのように対処すべきかを実際の検証結果とともに解説します。
概要
S3 バケットに保存されたGZIPファイル(.gz)をブラウザからダウンロードすると、以下のような状況が発生することがあります。
- ファイル名は example.txt.gz のままだが、中身は解凍されたテキストになっている
- ファイルサイズが元の圧縮ファイルより大きくなっている
- 解凍ソフトで開こうとするとエラーになる
この現象は、HTTP の仕様とブラウザの挙動、そして S3 のメタデータにより発生します。
なぜ起こるのか
HTTP通信における圧縮の仕組み
HTTPプロトコルでは、通信効率化のために以下のヘッダーが使用されます。
- Accept-Encoding: クライアント(ブラウザ)が対応している圧縮形式をサーバーに伝える
- Content-Encoding: サーバーが実際に使用した圧縮形式をクライアントに伝える
ブラウザは Content-Encoding: gzip ヘッダーを受け取ると、そのコンテンツを自動的に解凍して表示・保存します。これはWebページの転送効率化のための正常な動作になります。
S3 と Content-Encoding の関係
S3 にオブジェクトをアップロードする際、明示的に Content-Encoding メタデータを設定できます。また、Kinesis Data Firehose などの AWS サービスが S3 にデータを書き込む際、GZIP 圧縮を有効にすると自動的に Content-Encoding: gzip メタデータが設定されます。
このメタデータが設定されたオブジェクトをブラウザでダウンロードすると、ブラウザは HTTP の仕様に従って自動的に解凍処理を行います。その結果、ファイル名は .gz のままでも、中身は解凍された状態になります。
検証
4つのパターンで S3 に GZIP ファイルをアップロードし、Chrome と Safari でダウンロード動作を検証しました。
検証環境
- テスト用S3バケット: browser-download-test-bucket
- 元ファイル: 簡単なテキストファイル(test.txt)をGZIP圧縮したもの
検証パターン
- 通常の GZIP ファイル(メタデータ指定なし)
- Content-Encoding を明示的に設定(Content-Encoding: gzip)
- Content-Disposition を設定(Content-Disposition: attachment; filename=test.txt.gz)
- Content-Type と Content-Encoding の組み合わせ(Content-Type: text/plainとContent-Encoding: gzip)
検証結果
パターン1: 通常のGZIPファイル(メタデータ指定なし)
Chrome & Safari:
- ダウンロードファイル名: test.txt.gz
- ファイル状態: 圧縮されたまま(バイナリ)
- ファイルサイズ: 元のGZIPファイルと同じ
- 解凍ソフトで正常に開ける
パターン2: Content-Encoding を明示的に設定
Chrome & Safari:
- ダウンロードファイル名: test.txt.gz
- ファイル状態: 解凍済み(テキスト)
- ファイルサイズ: 元のテキストファイルと同じ(圧縮前のサイズ)
- 解凍ソフトでは開けない(すでに解凍済みのため)
Content-Encoding: gzipが設定されているため、ブラウザは自動的に解凍処理を行いました。ファイル名の拡張子は.gzのままですが、中身はすでに解凍されています。
パターン3: Content-Disposition を設定
Chrome & Safari:
- ダウンロードファイル名: test.txt.gz(Content-Dispositionで指定した名前)
- ファイル状態: 圧縮されたまま(バイナリ)
- ファイルサイズ: 元のGZIPファイルと同じ
- 解凍ソフトで正常に開ける
Content-Disposition ヘッダーを設定しても、Content-Encoding がない場合は自動解凍は行われません。
パターン4: Content-TypeとContent-Encoding の組み合わせ
Chrome & Safari:
- ダウンロードファイル名: test.txt.gz
- ファイル状態: 解凍済み(テキスト)
- ファイルサイズ: 元のテキストファイルと同じ(圧縮前のサイズ)
- Content-Type: text/plainのため、ブラウザによっては直接表示しようとする場合もある
Content-Encoding: gzip が設定されているため自動解凍が行われ、さらにContent-Type: text/plain によってテキストファイルとして認識されます。
検証まとめ
検証から明らかになったのは、S3 オブジェクトに Content-Encoding: gzip メタデータが設定されている場合、ブラウザは拡張子に関わらず自動的に解凍処理を行うという点です。
これは HTTP プロトコルの仕様に基づく正常な動作ですが、ファイル名と中身の不一致を引き起こす原因となります。
問題の回避方法
この問題を回避するには、以下のような方法が有効になります。
1. AWS CLI や SDK を使用してダウンロードする
ブラウザを介さずに AWS CLI でダウンロードすれば、自動解凍は行われません。
aws s3 cp s3://bucket-name/path/to/file.gz ./local-file.gz
2. S3 オブジェクトのメタデータを修正する
既存のオブジェクトから Content-Encoding メタデータを削除するか、新規アップロード時に適切に設定します。
# メタデータを指定せずにアップロード
aws s3 cp local-file.gz s3://bucket-name/path/to/file.gz
# 既存オブジェクトのメタデータを修正(コピーして上書き)
aws s3 cp s3://bucket-name/path/to/file.gz s3://bucket-name/path/to/file.gz --metadata-directive REPLACE --content-encoding ""
3. 署名付き URL を使用する
特定のヘッダーを指定した署名付き URL を生成することで、ダウンロード時の挙動を制御できます。
aws s3 presign s3://bucket-name/path/to/file.gz --expires-in 3600
4. S3 バケットのウェブホスティング設定を活用する
静的ウェブサイトホスティングを有効にし、適切な Content-Type マッピングを設定することで、特定のファイルタイプの扱いを制御できます。
まとめ
S3 からブラウザで GZIP ファイルをダウンロードする際、Content-Encoding メタデータの設定によっては自動的に解凍されることがあります。これは HTTP プロトコルの仕様に基づく動作ですが、ファイル名と中身の不一致という混乱を招く可能性があります。
この問題を理解し、適切な対処法を知っておくことで、S3 を使ったファイル管理をより確実に行うことができます。特に自動処理やバッチ処理を行う場合は、AWS CLI や SDK の使用を検討するか、メタデータの設定に十分注意しましょう。
参考文献
- Content-Encoding - HTTP | MDN
- オブジェクトメタデータの使用 - Amazon Simple Storage Service
- HTTP エンドポイント配信リクエストとレスポンスの仕様を理解する - Amazon Data Firehose
アノテーション株式会社について
アノテーション株式会社はクラスメソッドグループのオペレーション専門特化企業です。サポート・運用・開発保守・情シス・バックオフィスの専門チームが、最新 IT テクノロジー、高い技術力、蓄積されたノウハウをフル活用し、お客様の課題解決を行っています。当社は様々な職種でメンバーを募集しています。「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、アノテーション株式会社 採用サイトをぜひご覧ください。