[アップデート] Amazon S3 のオブジェクトコピー API の条件付き書き込みオプションが追加されたので従来のオプションと何が違うのか確認してみた

[アップデート] Amazon S3 のオブジェクトコピー API の条件付き書き込みオプションが追加されたので従来のオプションと何が違うのか確認してみた

2025.10.31

いわさです。

先日、Amazon S3 のコピー API に条件付き書き込み機能が追加されたというアナウンスがありました。

https://aws.amazon.com/about-aws/whats-new/2025/10/amazon-s3-conditional-write-functionality-copy-operations/

あれ?それって前からできなかったっけと思った方も多いかもしれません。
そうなのです条件付き書き込み自体は前から一応できていたんですよね。

S3 の条件付きリクエスト関係だと、GetObject/HeadObject API についてはIf-MatchIf-None-Matchヘッダーを使うことが出来ていました。
CopyObjectについてもx-amz-copy-source-if-matchx-amz-copy-source-if-none-matchなどのカスタムヘッダーであればこれまでも使うことが出来ていました。

何が変わったのかよくわからなかったので少し調べてみました。

CopyObject も RFC 7232 準拠のヘッダーをサポート

今回のアップデートで、CopyObject についても API が拡張され、カスタムヘッダーではなくIf-MatchIf-None-Matchを使うことが出来るようになりました。
また、AWS CLI などのツールについても従来に加えてオプションが追加されていました。

CB39AECC-9B74-4032-8F2D-6B9263AB1E4C.png
copy-object — AWS CLI 2.31.25 Command Reference より

まず、これまでも CopyObject は条件付きリクエストをサポートしていたがカスタムヘッダーでの対応だったのが、RFC 7232 準拠ヘッダーをサポートしたという点を知っておきましょう。

挙動の違い

少し試してみたのですが、本日時点だと少し挙動が怪しかったです。
ドキュメントの記述も従来のヘッダーと同じように見えるのですが、挙動と異なっていそうな点もあり...

まず S3 バケットを 2 つ作成してみます。

% aws s3 mb s3://hoge1031bucket1 --profile hoge
make_bucket: hoge1031bucket1
% aws s3 mb s3://hoge1031bucket2 --profile hoge
make_bucket: hoge1031bucket2

バケット 1 にファイルをアップロードしましょう。ETAG はこんな感じ。

% aws s3 cp hoge.txt s3://hoge1031bucket1/ --profile hoge
upload: ./hoge.txt to s3://hoge1031bucket1/hoge.txt
% aws s3api list-objects-v2 --bucket hoge1031bucket1 --profile hoge
{
    "Contents": [
        {
            "Key": "hoge.txt",
            "LastModified": "2025-10-30T20:27:17+00:00",
            "ETag": "\"47bce5c74f589f4867dbd57e9ca9f808\"",
            "ChecksumAlgorithm": [
                "CRC64NVME"
            ],
            "ChecksumType": "FULL_OBJECT",
            "Size": 3,
            "StorageClass": "STANDARD"
        }
    ],
    "RequestCharged": null,
    "Prefix": ""
}

バケット 1 のファイルをバケット 2 に別名でコピーします。
別名なのですが、ETAG は同じです。

% aws s3api copy-object --bucket hoge1031bucket2 --copy-source hoge1031bucket1/hoge.txt --key copy.txt --profile hoge
{
    "ServerSideEncryption": "AES256",
    "CopyObjectResult": {
        "ETag": "\"47bce5c74f589f4867dbd57e9ca9f808\"",
        "LastModified": "2025-10-30T20:32:30+00:00",
        "ChecksumCRC64NVME": "Rnr2/5P7Gsk="
    }
}
% aws s3api copy-object --bucket hoge1031bucket2 --copy-source hoge1031bucket1/hoge.txt --key copy2.txt --profile hoge
{
    "ServerSideEncryption": "AES256",
    "CopyObjectResult": {
        "ETag": "\"47bce5c74f589f4867dbd57e9ca9f808\"",
        "LastModified": "2025-10-30T20:32:35+00:00",
        "ChecksumCRC64NVME": "Rnr2/5P7Gsk="
    }
}
% aws s3api list-objects-v2 --bucket hoge1031bucket2 --profile hoge
{
    "Contents": [
        {
            "Key": "copy.txt",
            "LastModified": "2025-10-30T20:32:30+00:00",
            "ETag": "\"47bce5c74f589f4867dbd57e9ca9f808\"",
            "ChecksumAlgorithm": [
                "CRC64NVME"
            ],
            "ChecksumType": "FULL_OBJECT",
            "Size": 3,
            "StorageClass": "STANDARD"
        },
        {
            "Key": "copy2.txt",
            "LastModified": "2025-10-30T20:32:35+00:00",
            "ETag": "\"47bce5c74f589f4867dbd57e9ca9f808\"",
            "ChecksumAlgorithm": [
                "CRC64NVME"
            ],
            "ChecksumType": "FULL_OBJECT",
            "Size": 3,
            "StorageClass": "STANDARD"
        }
    ],
    "RequestCharged": null,
    "Prefix": ""
}

まずは従来のカスタムヘッダーを使う、--copy-source-if-none-matchを試してみましょう。
新しい複製を行うのですが、先程と異なり ETAG を指定します。

% aws s3api copy-object --bucket hoge1031bucket2 --copy-source hoge1031bucket1/hoge.txt --key copy3.txt --copy-source-if-none-match "47bce5c74f589f4867dbd57e9ca9f808" --profile hoge

An error occurred (PreconditionFailed) when calling the CopyObject operation: At least one of the pre-conditions you specified did not hold

if-none-match ではもし存在しなければコピーができるという条件になるのでエラーになりました。キー名関係なく、ETAG値で存在チェックしてくれていますね。

一方で指定した ETAG が存在しない場合はコピーが出来ます。

% aws s3api copy-object --bucket hoge1031bucket2 --copy-source hoge1031bucket1/hoge.txt --key copy3.txt --copy-source-if-none-match "47bce5c74f589f4867dbd57e9ca9f807" --profile hoge
{
    "ServerSideEncryption": "AES256",
    "CopyObjectResult": {
        "ETag": "\"47bce5c74f589f4867dbd57e9ca9f808\"",
        "LastModified": "2025-10-30T20:37:36+00:00",
        "ChecksumCRC64NVME": "Rnr2/5P7Gsk="
    }
}

これが新しく追加された--if-none-matchだとどうなるのかを試してみました。

% aws s3api copy-object --bucket hoge1031bucket2 --copy-source hoge1031bucket1/hoge.txt --key copy3.txt --if-match "47bce5c74f589f4867dbd57e9ca9f808" --profile hoge
{
    "ServerSideEncryption": "AES256",
    "CopyObjectResult": {
        "ETag": "\"47bce5c74f589f4867dbd57e9ca9f808\"",
        "LastModified": "2025-10-30T20:42:26+00:00",
        "ChecksumCRC64NVME": "Rnr2/5P7Gsk="
    }
}
% aws s3api copy-object --bucket hoge1031bucket2 --copy-source hoge1031bucket1/hoge.txt --key copy3.txt --if-none-match "47bce5c74f589f4867dbd57e9ca9f808" --profile hoge

An error occurred (NotImplemented) when calling the CopyObject operation: A header you provided implies functionality that is not implemented

色々なパターンを試してみたのですが、なんかうまく動かないですね。
A header you provided implies functionality that is not implementedなのでIf-None-Matchヘッダー自体が有効じゃないような感じが...

一方でIf-Matchについては使えるようでして--copy-source-if-matchと比較したのが以下です。

# --copy-source-if-match
% aws s3api copy-object --bucket hoge1031bucket2 --copy-source hoge1031bucket1/hoge.txt --key copy3.txt --copy-source-if-match "47bce5c74f589f4867dbd57e9ca9f808" --profile hoge
{
    "ServerSideEncryption": "AES256",
    "CopyObjectResult": {
        "ETag": "\"47bce5c74f589f4867dbd57e9ca9f808\"",
        "LastModified": "2025-10-30T20:43:23+00:00",
        "ChecksumCRC64NVME": "Rnr2/5P7Gsk="
    }
}
% aws s3api copy-object --bucket hoge1031bucket2 --copy-source hoge1031bucket1/hoge.txt --key copy4.txt --copy-source-if-match "47bce5c74f589f4867dbd57e9ca9f808" --profile hoge
{
    "ServerSideEncryption": "AES256",
    "CopyObjectResult": {
        "ETag": "\"47bce5c74f589f4867dbd57e9ca9f808\"",
        "LastModified": "2025-10-30T20:43:33+00:00",
        "ChecksumCRC64NVME": "Rnr2/5P7Gsk="
    }
}
% aws s3api copy-object --bucket hoge1031bucket2 --copy-source hoge1031bucket1/hoge.txt --key copy5.txt --copy-source-if-match "47bce5c74f589f4867dbd57e9ca9f808" --profile hoge
{
    "ServerSideEncryption": "AES256",
    "CopyObjectResult": {
        "ETag": "\"47bce5c74f589f4867dbd57e9ca9f808\"",
        "LastModified": "2025-10-30T20:43:47+00:00",
        "ChecksumCRC64NVME": "Rnr2/5P7Gsk="
    }
}
# --if-match
% aws s3api copy-object --bucket hoge1031bucket2 --copy-source hoge1031bucket1/hoge.txt --key copy5.txt --if-match "47bce5c74f589f4867dbd57e9ca9f808" --profile hoge      
{
    "ServerSideEncryption": "AES256",
    "CopyObjectResult": {
        "ETag": "\"47bce5c74f589f4867dbd57e9ca9f808\"",
        "LastModified": "2025-10-30T20:43:59+00:00",
        "ChecksumCRC64NVME": "Rnr2/5P7Gsk="
    }
}
% aws s3api copy-object --bucket hoge1031bucket2 --copy-source hoge1031bucket1/hoge.txt --key copy6.txt --if-match "47bce5c74f589f4867dbd57e9ca9f808" --profile hoge

An error occurred (NoSuchKey) when calling the CopyObject operation: The specified key does not exist.

ETAG が一致していればコピーできるという点は概ね同じなのですが、--if-matchの場合はキーが存在しない場合はエラーになりました。

さいごに

本日は Amazon S3 のオブジェクトコピー API の条件付き書き込みオプションが追加されたので従来のオプションと何が違うのか確認してみました。

まず、アナウンスやドキュメントベースでいうと、従来の GetOjbect や HeadObject と同じように RFC 7232 準拠のIf-MatchIf-None-Matchが CopyObject でもサポートされたというものになっています。
なお、公式ドキュメントのこのヘッダー周りについては以下に記載されていまして、今回のものが追記されています。

https://docs.aws.amazon.com/AmazonS3/latest/userguide/conditional-reads.html

ただ、検証結果としては本日時点の AWS CLI v2 の最新バージョンで試した限りではIf-None-Matchについては期待どおり動作せず、If-Matchについてはx-amz-copy-source-if-matchと挙動が異なっていました。
もう少し評価が必要ですが、そのまま従来の API から置き換えれる雰囲気ではなさそうなので確認して使ったほうが良さそうですね。

この記事をシェアする

FacebookHatena blogX

関連記事