1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

boto3でS3に一時アクセスする generate_presigned_url の使い方

Posted at

generate_presigned_urlとは?

boto3generate_presigned_urlメソッドは、S3クライアントを使って、特定のS3操作(例:get_objectput_object)のための署名付きURLを生成します。このURLにはAWSの認証情報に基づく署名が含まれており、指定した有効期限内でのみアクセスが可能です。主な用途は、プライベートバケットのファイルを一時的に共有したり、クライアントが直接S3にアップロードできるようにすること。

主な特徴

  • 一時的アクセス: 有効期限(秒単位で指定)を設定可能。期限後はURLが無効に。
  • セキュリティ: AWSのSignature Version 4で署名を生成し、改ざん防止。
  • 柔軟性: ダウンロード(GET)、アップロード(PUT)、削除(DELETE)など、さまざまなS3操作に対応。
  • 簡単さ: 数行のコードでURLを生成可能。

基本的な使い方

generate_presigned_urlの基本構文は以下の通りです

import boto3

s3_client = boto3.client('s3')
url = s3_client.generate_presigned_url(
    ClientMethod='method_name',  # 実行するS3操作(例:get_object, put_object)
    Params={'Bucket': 'bucket_name', 'Key': 'object_key'},  # S3操作のパラメータ
    ExpiresIn=3600  # 有効期限(秒)
)

パラメータの詳細

  1. ClientMethod (必須):

    • S3の操作を指定。主な値:
      • 'get_object': ファイルのダウンロード。
      • 'put_object': ファイルのアップロード。
      • 'delete_object': ファイルの削除。
    • 他にもhead_object(メタデータ取得)など、S3のAPIに対応したメソッドを指定可能。
  2. Params (必須):

    • S3操作に必要なパラメータを辞書形式で指定。
    • 必須キー:
      • Bucket: 対象のS3バケット名。
      • Key: 対象のオブジェクトキー(ファイルのパス)。
    • 任意キー(例):
      • ContentType: アップロード時のファイル形式(例:'image/jpeg')。
      • Metadata: アップロード時に付与するメタデータ。
      • ResponseContentDisposition: ダウンロード時のファイル名指定(例:'attachment; filename="download.pdf"')。
  3. ExpiresIn (必須):

    • URLの有効期限(秒単位)。最大は7日(604800秒)だが、セキュリティのため短めに設定推奨(例:数分~数時間)。
  4. HttpMethod (任意):

    • HTTPメソッドを明示的に指定(例:'GET''PUT')。通常はClientMethodから自動推論されるので省略可。

具体例と実装

プロジェクトで実際に使った例を基に、ダウンロードとアップロードのケースを解説します。プロジェクトでは、S3のプライベートバケットにあるPDFや画像をチームでプレビューするためのビューワーを作っていて、署名付きURLで安全にアクセスを実現した。

1. ダウンロード用URL(get_object

チームがS3のレポートPDFをプレビューできるように、get_object用の署名付きURLを生成。

import boto3
from botocore.exceptions import ClientError

# ローカル開発用にプロファイルを指定
session = boto3.Session(profile_name='my-profile')
s3_client = session.client('s3')

try:
    url = s3_client.generate_presigned_url(
        ClientMethod='get_object',
        Params={
            'Bucket': 'my-project-bucket',
            'Key': 'reports/client-report.pdf',
            'ResponseContentDisposition': 'inline'  # ブラウザで直接表示
        },
        ExpiresIn=3600  # 1時間有効
    )
    print("ダウンロードURL:", url)
except ClientError as e:
    print(f"エラー: {e}")

ポイント

  • ResponseContentDisposition: 'inline'を指定すると、PDFがブラウザで直接開く。'attachment; filename="report.pdf"'にすればダウンロードになる。
  • 有効期限: プレビュー用途なので1時間に設定。短いほうがセキュリティ的に安心。
  • エラーハンドリング: ClientErrorでIAM権限エラーやバケットが存在しない場合をキャッチ。

2. アップロード用URL(put_object

ユーザーがウェブアプリから画像を直接S3にアップロードできるように、put_object用の署名付きURLを生成。

import boto3
from botocore.exceptions import ClientError

session = boto3.Session(profile_name='my-profile')
s3_client = session.client('s3')

try:
    url = s3_client.generate_presigned_url(
        ClientMethod='put_object',
        Params={
            'Bucket': 'my-project-bucket',
            'Key': f'uploads/user-{user_id}/image.jpg',
            'ContentType': 'image/jpeg',
            'Metadata': {'uploaded-by': user_id}
        },
        ExpiresIn=1800  # 30分有効
    )
    print("アップロードURL:", url)
except ClientError as e:
    print(f"エラー: {e}")

クライアント側でのアップロード例(TypeScript)

フロントエンドでこのURLを使ってアップロード:

import axios from 'axios';

const uploadFile = async (file: File, presignedUrl: string) => {
  try {
    const response = await axios.put(presignedUrl, file, {
      headers: { 'Content-Type': 'image/jpeg' }
    });
    console.log('アップロード成功:', response.status);
  } catch (error) {
    console.error('アップロード失敗:', error);
  }
};

ポイント

  • ContentType: アップロードするファイルのMIMEタイプを指定。クライアント側も同じContent-Typeヘッダーを設定しないと、S3が正しく認識しない。
  • Metadata: アップロード時にカスタムメタデータを付与可能。後で誰がアップロードしたか追跡するのに便利。
  • 有効期限: アップロードは短時間で済むので30分に設定。

3. その他の操作(例:削除)

削除用の署名付きURLも生成可能。例:

url = s3_client.generate_presigned_url(
    ClientMethod='delete_object',
    Params={'Bucket': 'my-project-bucket', 'Key': 'reports/old-file.pdf'},
    ExpiresIn=600  # 10分有効
)

クライアント側でDELETEリクエストを送れば削除可能。ただし、削除は慎重に(間違って消すと復元できない)。


ハマったポイントと対策

実装中にハマったことをメモ。

  1. クレデンシャルエラー
    ローカル環境でNoCredentialsErrorが出て詰まった。AWS CLIでaws configure --profile my-profileを設定し、コードでboto3.Session(profile_name='my-profile')を指定することで解決。デプロイ環境ではEC2やECSのIAMロールを使ったので、この問題はローカル限定だった。

  2. IAM権限
    s3:GetObjects3:PutObjectの権限がなくて「Access Denied」。IAMポリシーを確認し、以下を追加:

    {
        "Effect": "Allow",
        "Action": ["s3:GetObject", "s3:PutObject", "s3:ListBucket"],
        "Resource": ["arn:aws:s3:::my-project-bucket", "arn:aws:s3:::my-project-bucket/*"]
    }
    
  3. Content-Typeのミスマッチ
    アップロード時にContentTypeを指定しないと、S3がデフォルトでapplication/octet-streamに設定。クライアント側と一致させるため、ParamsContentTypeを明示。

  4. 有効期限の設定
    最初、1日(86400秒)にしたら長すぎるとチームから指摘。プレビューなら5~10分、アップロードなら30分くらいで十分。用途に応じて調整した。

  5. ファイルキーのエンコーディング
    ファイルキーにスラッシュや特殊文字がある場合、URLエンコーディングに注意。FastAPIの{key:path}が上手く処理してくれたが、特殊文字が多い場合はurllib.parse.quoteを検討。


ベストプラクティス

  • 有効期限を短く: セキュリティのため、必要最低限の時間(例:5分~1時間)に設定。
  • クレデンシャル管理: ローカルではprofile_name、本番ではIAMロールを使う。アクセスキー直書きは絶対NG。
  • エラーハンドリング: ClientErrorをキャッチして、ユーザー向けに分かりやすいエラーメッセージを返す。
  • ログ: 生成したURLやエラーをログに残すと、デバッグが楽。
  • Content-Type: アップロード時はクライアントとサーバーで一致させる。MIMEタイプのリストを事前に決めておくと安心。
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?