Python
 Computer >> コンピューター >  >> プログラミング >> Python

PythonのBoto3ライブラリを使ってAWS S3にオブジェクトをアップロードする方法

課題

PythonのBoto3ライブラリを使用して、AWS S3にオブジェクトをアップロードする方法を解説します。ここでは、ローカルにある test.zip をS3バケット「Bucket_1」にアップロードするケースを例に取り上げます。

前提条件

コードを実行する前に、以下の準備が必要です。

  • AWSクレデンシャル(アクセスキー・シークレットキー)が ~/.aws/credentials や環境変数などで設定済みであること
  • 対象バケットへの s3:PutObject 権限を持つIAMポリシーが割り当てられていること
  • pip install boto3 によりBoto3がインストール済みであること

解決手順(アプローチ/アルゴリズム)

  1. ステップ1: boto3 と botocore.exceptions(ClientError)をインポートし、例外ハンドリングに備えます。
  2. ステップ2: pathlib から PurePosixPath をインポートし、パスからファイル名を取得できるようにします。
  3. ステップ3: 関数 upload_object_into_s3 の引数として、s3_path(S3上の保存先パス)と filepath(ローカルファイルのパス)を受け取ります。
  4. ステップ4: s3_path が s3://bucket_name/key 形式、filepath が C://users/filename のようなローカルパス形式であることを検証します。
  5. ステップ5: boto3.session.Session() でAWSセッションを作成します。
  6. ステップ6: S3用のAWSリソース(session.resource('s3'))を生成します。
  7. ステップ7: S3パスを「/」で分割し、ルートのバケット名とキーパスを切り出します。
  8. ステップ8: 完全なファイルパスからファイル名を取得し、S3のキーパスに連結します。
  9. ステップ9: upload_fileobj 関数を使って、ローカルファイルをS3へアップロードします。
  10. ステップ10: wait_until_exists 関数で、アップロード処理の完了を待機します。
  11. ステップ11: レスポンスコードに基づく例外処理によって、ファイルが正常にアップロードされたかどうかを判定します。
  12. ステップ12: アップロード中に予期しない問題が発生した場合に備え、汎用的な例外処理を行います。

サンプルコード

以下のコードで、AWS S3にファイルをアップロードできます。

import boto3
from botocore.exceptions import ClientError
from pathlib import PurePosixPath

def upload_object_into_s3(s3_path, filepath):

    if 's3://' in filepath:
        print('SourcePath is not a valid path.' + filepath)
        raise Exception('SourcePath is not a valid path.')
    elif s3_path.find('s3://') == -1:
        print('DestinationPath is not a s3 path.' + s3_path)
        raise Exception('DestinationPath is not a valid path.')

    session = boto3.session.Session()
    s3_resource = session.resource('s3')

    # S3パスを分割してバケット名とキーパスを取得
    tokens = s3_path.split('/')
    target_key = ''
    if len(tokens) > 3:
        for tokn in range(3, len(tokens)):
            if tokn == 3:
                target_key += tokens[tokn]
            else:
                target_key += '/' + tokens[tokn]
    target_bucket_name = tokens[2]

    # ローカルパスからファイル名を取得してキーパスに連結
    file_name = PurePosixPath(filepath).name
    if target_key != '':
        key_path = target_key + '/' + file_name
    else:
        key_path = file_name

    print(('key_path: ' + key_path, 'target_bucket: ' + target_bucket_name))

    try:
        # ローカルパスのファイルをS3へアップロード
        with open(filepath, 'rb') as file:
            s3_resource.meta.client.upload_fileobj(file, target_bucket_name, key_path)
        try:
            # オブジェクトが存在するようになるまで待機
            s3_resource.Object(target_bucket_name, key_path).wait_until_exists()
        except ClientError as error:
            error_code = int(error.response['Error']['Code'])
            if error_code == 412 or error_code == 304:
                print("Object didn't Upload Successfully ", target_bucket_name)
                raise error
        return 'Object Uploaded Successfully'
    except Exception as error:
        print('Error in upload object function of s3 helper: ' + error.__str__())
        raise error

print(upload_object_into_s3('s3://Bucket_1/testfolder', 'c://test.zip'))

実行結果

key_path:/testfolder/test.zip, target_bucket: Bucket_1
Object Uploaded Successfully

コードのポイント

  • upload_fileobj は開かれたファイルオブジェクトをそのまま受け取るため、メモリ上のデータやストリームを扱う場合に便利です。単純にローカルパスからアップロードするだけなら、よりシンプルな upload_file の利用も検討できます。
  • wait_until_exists は、オブジェクトが実際にS3上に存在することを確認できるまでポーリングを行うため、アップロードの確実性を高められます。
  • ClientError のレスポンスコード(412・304など)を判定することで、条件付きアップロードの失敗などの状況を細かくハンドリングできます。
  • with 文を使ってファイルを開いているため、処理終了後はファイルが自動的にクローズされます。
  1. Pythonでファイル拡張子を抽出する方法【os.path.splitextの使い方】

    Pythonでは、標準ライブラリの os.path.splitext メソッドを使うことで、ファイル名の文字列から拡張子を簡単に抽出できます。このメソッドは、パス名を (root, ext) のペア(タプル)に分割します。分割後も root + ext == path の関係が成り立ちます。また、ext は空文字列であるか、ピリオド(.)で始まり、最大1つのピリオドのみを含む文字列になります。基本的な使い方以下は、ファイル名から拡張子を抽出するシンプルな例です。import os file_name = my_file.txt print(os.path.splitext(file_name

  2. Pythonで複数のファイル名を再帰的に一括変更する方法

    Pythonでは、os.walkを使ってディレクトリを再帰的に走査し、見つかったファイルに対してos.renameを適用することで、フォルダ内の複数のファイル名をまとめて変更できます。ここでは、特定の文字列を別の文字列に置き換える形でファイル名を一括リネームする方法を紹介します。 コード例 import os def replace(folder_path, old, new): for path, subdirs, files in os.walk(folder_path): for name in files: if(old.lower