はじめに
Amazon S3(以下、S3)へのオブジェクトアップロード、普段やっている方は多いと思います。容量が大きいファイルをアップロードしたり、ネットワーク環境が良くない場合は、マルチパートアップロードを推奨されています。「マルチパートアップロード」という言葉を認定試験の勉強で見た方は多いと思います。私も認定試験の勉強で言葉とその意味を知ってから、実際にマルチパートアップロードをやったことはありませんでした。
今回、気になることがあり実際にマルチパートアップロードをやってみました。S3にマルチパートアップロードでオブジェクトをアップロードするときのAPIの挙動を実際に試してみた結果と、得た気づきと疑問について話します。
本記事は、筆者個人の見解です。個人利用の範囲で試した結果のため、参考程度に留めてください。内容に間違い等がありましたらご指摘いただけますと幸いです。
書かないこと
- 今回の動作検証に使ったS3バケットの作成手順
- デフォルト設定で作成したため割愛します
- CloudTrailの設定及びAthenaでの検索設定の手順
- 今回の話の本筋から離れるため割愛します
今回の動作検証環境
- Windows11 Pro
- WSL
- AWS CLI バージョン2.34.14
- S3バケット
- デフォルト設定のまま作成(本記事公開後に削除)
マルチパートアップロードとは
「マルチパートアップロード」は、1つのオブジェクトを複数の部品(パート)に分けてアップロードして最後に元のオブジェクトに戻すといったアップロード方法です。
マルチパートアップロードの利点
アップロードのスループットを向上し、不安定なネットワーク環境でも負荷の少ないアップロードを期待できます。
- パートを並列にアップロードしてスループットを向上
- ネットワークエラーにより失敗したアップロードを再開する際の影響を最小限に抑える
- オブジェクトアップロードの一時停止と再開を任意のタイミングにすることができる
- オブジェクトを作成しながらアップロードできる
マルチパートアップロードには終了期限がありません。一度始めたら明示的にマルチパートアップロードを完了または停止する必要があります。
なぜ今マルチパートアップロードを調べたのか?
AWS CLIの構成について気になり調べていたのがキッカケです。AWS CLIにはS3関連のコマンドが6種類あります。これは他のサービスにはない特徴だと思います。
- s3
- s3api
- s3control
- s3outposts
- s3tables
- s3vectors
s3outposts/s3tables/s3vector が分かれているのはなんとなく理解できるのですが、
特にs3とs3apiが何で分かれているのかが気になり、Kiroに聞いてみました。
以下のような回答(抜粋)がKiroから返ってきました。
AWS CLI の S3 コマンドは公式に 3 つのセットに分かれています:
s3 高レベル よく使う操作を抽象化したもの
s3api 低レベル Amazon S3 REST API と 1:1 対応
s3control 低レベル S3 Control API と 1:1 対応(Access Grants、Storage Lens 等)
分けられた経緯・設計思想
SDK のドキュメントにその背景が明確に書かれています。
S3 の操作には「マルチパートアップロード」のような複数ステップが必要な処理があります。
低レベル API では:
アップロードを開始する操作
パートをアップロードする操作
アップロードを完了する操作
という 3 ステップが必要です。これを毎回書くのは煩雑なので、高レベルインターフェースとして
1 回の呼び出しで完結できる s3 コマンドが作られました。
つまり「よくある操作を簡単にする」という UX 上の理由で s3 が生まれ、
細かい制御が必要なユーザー向けに s3api が残された、という構造です。
マルチパートアップロードって、s3apiの複数処理が必要なのか!?
とここで初めて知りました。では一度挙動を確認してみよう!と思い、実際にやってみた次第です。
KiroにAWS Knowledge MCP Serverを使わせて調べました。今回はKiroの回答を信じて動作確認をしましたが、公式ドキュメントを参照させたとはいえ、実際には内容の真偽を必ず確認しましょう。
マルチパートアップロードの挙動を確かめてみた
公式ドキュメントにマルチパートアップロードのチュートリアルが載っていました。これを参考に進めました。
テスト用ファイルの準備
以下のPowerShellコマンドで、15MBのテスト用ファイルを作成します。3つテスト用ファイルを作成しました(中身はすべて同じ)。
$bytes = New-Object byte[] (15 * 1024 * 1024)
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
[System.Convert]::ToBase64String($bytes).Substring(0, 15728640) | Set-Content ファイル名.拡張子 -NoNewline
公式ドキュメントがLinuxでのテストファイル作成コマンドが書かれていましたが、文字化けしてしまいました。テスト用ファイルなので気にする必要はないのですが、何となく気になりPowerShellで作成しました。
次にテスト用ファイルを5MBごとに分割します。
split -b 5M -d multipart-upload-test-CLI-LowLv.txt multipart-upload-test-CLI-LowLv-
ファイルが3つに分割されました。これをマルチパートアップロードの際にパートとして使います。

マルチパートアップロード実行
マルチパートアップロードを作成します。
aws s3api create-multipart-upload \
--bucket BucketName \
--key 'multipart-upload-test-CLI-LowLv.txt' \
--checksum-algorithm sha256
以下の通り表示されればOKです。
{
"ServerSideEncryption": "AES256",
"ChecksumAlgorithm": "SHA256",
"ChecksumType": "COMPOSITE",
"Bucket": "BucketName",
"Key": "multipart-upload-test-CLI-LowLv.txt",
"UploadId": "xxxxxxxxxxx・・・xxxxxxxxx"
}
次に、マルチパートアップロードするオブジェクトと紐づけたパートをアップロードします。あらかじめパートを3つに分割しましたので、for文で3回実行させました。
for i in {0..2}
do
aws s3api upload-part \
--bucket BucketName \
--key 'multipart-upload-test-CLI-LowLv.txt' \
--part-number $((i+1)) \
--body multipart-upload-test-CLI-LowLv-0${i} \
--upload-id "xxxxxxxxxxx・・・xxxxxxxxx" \
--checksum-algorithm SHA256
done
- part-number は、1から始まります。
- パートは、00から始まります。
成功したら、以下のように表示されます。
{
"ServerSideEncryption": "AES256",
"ETag": "\"yyyyy・・・yyyyyyy\"",
"ChecksumSHA256": "zzzzzzzzzzz・・・zzzzzzzzzzz"
}
{
"ServerSideEncryption": "AES256",
"ETag": "\"yyyyy・・・yyyyxxxx\"",
"ChecksumSHA256": "zzzzzzzzzzz・・・zzzzzxxxxxx"
}
{
"ServerSideEncryption": "AES256",
"ETag": "\"xxxxyy・・・yyyyxxxx\"",
"ChecksumSHA256": "yyyyyzzzzzz・・・zzzzzxxxxxx"
}
作成したパートを確認し、パート番号、ETag、チェックサム情報をJSONにアウトプットします。
aws s3api list-parts \
--bucket BucketName \
--key 'multipart-upload-test-CLI-LowLv.txt' \
--upload-id "xxxxxxxxxxx・・・xxxxxxxxx" \
--query '{Parts: Parts[*].{PartNumber: PartNumber, ETag: ETag, ChecksumSHA256: ChecksumSHA256}}' \
--output json > multipart.json
multipart.json
{
"Parts": [
{
"PartNumber": 1,
"ETag": "\"yyyyy・・・yyyyyyy\"",
"ChecksumSHA256": "zzzzzzzzzzz・・・zzzzzzzzzzz"
},
{
"PartNumber": 2,
"ETag": "\"yyyyy・・・yyyyxxxx\"",
"ChecksumSHA256": "zzzzzzzzzzz・・・zzzzzxxxxxx"
},
{
"PartNumber": 3,
"ETag": "\"xxxxyy・・・yyyyxxxx\"",
"ChecksumSHA256": "yyyyyzzzzzz・・・zzzzzxxxxxx"
}
]
}
アップロードしたパートを結合して1つのオブジェクトに戻します。
aws s3api complete-multipart-upload \
--bucket BucketName \
--key 'multipart-upload-test-CLI-LowLv.txt' \
--upload-id "xxxxxxxxxxx・・・xxxxxxxxx" \
--multipart-upload file://multipart.json
{
"ServerSideEncryption": "AES256",
"Location": "https://bucketname/multipart-upload-test-CLI-LowLv.txt",
"Bucket": "bucketname",
"Key": "multipart-upload-test-CLI-LowLv.txt",
"ETag": "\"xxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyzzzzzzzz\"",
"ChecksumSHA256": "zzzzzzzzzzzzzzxxxxxxxxxxxxxxxxxxx",
"ChecksumType": "COMPOSITE"
}
マルチパートアップロードにてオブジェクトがアップロードされたこと、確認できました。
確かにひとつひとつコマンド実行するのは手間ですね。
もっと簡単にマルチパートアップロードをできないのか?
公式ドキュメントを読む限り、マルチパートアップロードをする方法は、AWS CLI、AWS SDK、S3 REST APIを使ったアップロードの3通りです。マネジメントコンソールからマルチパートアップロードするための設定は無く、アップロードできるオブジェクトの最大サイズが160GBとなっています。また、160GBを超える場合は、AWS CLI、AWS SDK、S3 REST APIを使うよう記載されています。推測ですが、マネジメントコンソールから 160GB/オブジェクト までアップロードできることから、マルチパートアップロードする設計になっていないのでは?と考えています。
マネジメントコンソールからオブジェクトアップロードした時の挙動を確認しましたが、確かにマルチパートアップロードの挙動はしておらず、PutObjectでアップロードしていました。
マルチパートアップロードの簡単な方法は無さそうです。しかし、上述した通り、Kiroが「これを毎回書くのは煩雑なので、高レベルインターフェースとして 1 回の呼び出しで完結できる s3 コマンドが作られました。」と回答していました。もしかしたら、「s3(高レベルコマンド)で大容量のオブジェクトをコピーすると、マルチパートアップロードの挙動をするのでは?」 と思い、試してみようと思いました。
高レベルコマンドでアップロードした場合
aws s3 cp multipart-upload-test-CLI-HighLv.txt \
s3://bucketname/multipart-upload-test-CLI-HighLv.txt
上記コマンドを実行して最初に作成したテスト用オブジェクト(15MB)をアップロードしました。CloudTrailのログを確認したら、マルチパートアップロードが行われていたことが分かりました。以下2枚の画像の赤枠部分がaws s3 cpでコピーした時のログ、青枠がaws s3apiでマルチパートアップロードした時のログです。赤枠の方にはログ末尾にcommand#s3.cpと書かれています(画像2枚目の方)ので、aws s3 cpを実行したのが分かります。
ちなみに、容量が少ないファイルをアップロードしたところ、マルチパートアップロードではなく、PutObjectでアップロードされていました。
multipart-upload-test-CLI-HighLv-2.txt(12KB)のファイルをaws s3 cpでS3バケットにコピーします。
aws s3 cp multipart-upload-test-CLI-HighLv-2.txt \
s3://bucketname/multipart-upload-test-CLI-HighLv-2.txt
aws s3 cpで実行していますが、マルチパートアップロードではなくPutObjectでアップロードされていました。
高レベルコマンドでアップロードした場合、オブジェクト容量によって挙動が異なるのはなぜ??
これに関しては根拠となる公式ドキュメントの記載を見つけることができませんでした。従って、以降書くことを私の個人的見解です。参考程度に留めてください。また、ご存じの方がいらっしゃいましたら、ご教示いただけますと幸いです。
AWS CLIは、BotocoreというPythonのライブラリを使っています。これはBoto3でも使われています。
Boto3の公式ドキュメントより
Boto3 is built atop of a library called Botocore, which is shared by the AWS CLI. Botocore provides the low level clients, session, and credential & configuration data. Boto3 builds on top of Botocore by providing its own session, resources and collections.
(Boto3は、AWS CLIで共有されているBotocoreというライブラリをベースに構築されています。Botocoreは、低レベルのクライアント、セッション、認証情報および構成データを提供します。Boto3は、独自のセッション、リソース、コレクションを提供することで、Botocoreの上に構築されています。)
Boto3のドキュメントを読み進めていったところ、[S3 customization reference]のページに、
DEFAULTS = {'io_chunksize': 262144, 'max_bandwidth': None, 'max_concurrency': 10, 'max_io_queue': 100, 'max_io_queue_size': 100, 'max_request_concurrency': 10, 'multipart_chunksize': 8388608, 'multipart_threshold': 8388608, 'num_download_attempts': 5, 'preferred_transfer_client': 'auto', 'use_threads': True}
があり、その中に、
'multipart_chunksize': 8388608
'multipart_threshold': 8388608
と書かれていました。
8388608バイト、つまり、約8MBとなります。Boto3と同じライブラリを使っているAWS CLIも、8MB以上のファイルをaws s3 cpでアップロードする時は、8MBごとにパートを作って(最後のパートは8MBに満たなくても良い)アップロードするということになります。しかしながら、あくまでもBoto3のドキュメントであって、AWS CLIのリファレンスには同様の記載はありませんでした。aws s3 cpではマルチパートアップロードに関する細かい設定もできず、マルチパートアップロードに関する記述もありませんでした。それっぽい気はしますが、これが答えと言い切れず推測の域を出ませんでした。
個人的見解で恐縮ですが、aws s3 cpのような高レベルコマンドが色んな処理をよしなにまとめて実行してくれて、ユーザーを楽にしてくれているのかなぁと思いました。
最後に
何気なくやっているS3へのファイルアップロードでも、ファイルの容量、アップロード手法によって、裏で実行しているAPIの挙動に差が出ることを初めて知りました。改めて、AWSはAPIの集合体であることを認識しました。何気なくやっている作業を深掘りすると、気づきや新たな疑問に出会えますね。
この記事がどなたかの参考になれば幸いです。最後まで読んでいただき、ありがとうございました!!






