

# 如何使用有条件写入来防止对象覆盖
<a name="conditional-writes"></a>

通过使用有条件写入，可以向 `WRITE` 请求中添加一个额外的标头，以便为 Amazon S3 操作指定前提条件。要有条件地写入对象，请添加 HTTP `If-None-Match` 或 `If-Match` 标头。

`If-None-Match` 标头通过验证存储桶中尚不存在具有相同键名称的对象，来防止覆盖现有数据。

或者，您可以在写入对象之前添加 `If-Match` 标头来检查对象的实体标签（ETag）。通过此标头，Amazon S3 将提供的 ETag 值与 S3 中对象的 ETag 值进行比较。如果 ETag 值不匹配，则操作会失败。

存储桶拥有者可以使用存储桶策略对上传的对象强制实施有条件写入。有关更多信息，请参阅 [强制对 Amazon S3 存储桶实施有条件写入](conditional-writes-enforce.md)。

**注意**  
要使用有条件写入，必须使用 AWS 签名版本 4 对请求进行签名。

**Topics**
+ [如何防止基于键名称的对象覆盖](#conditional-write-key-names)
+ [如果对象已更改，如何防止被覆盖](#conditional-write-etags)
+ [有条件写入行为](#conditional-error-response)
+ [有条件写入场景](#conditional-write-scenarios)
+ [强制对 Amazon S3 存储桶实施有条件写入](conditional-writes-enforce.md)

## 如何防止基于键名称的对象覆盖
<a name="conditional-write-key-names"></a>

在创建对象或者将对象复制到目标存储桶之前，您可以使用 HTTP `If-None-Match` 条件标头，根据对象的键名称检查指定存储桶中是否已存在该对象。

具有 HTTP `If-None-Match` 标头的有条件写入将在 `WRITE` 操作期间检查对象是否存在。如果在存储桶中找到相同的键名称，则操作失败。在不使用 HTTP `If-None-Match` 标头时，如果在未受版本控制或已暂停版本控制的存储桶中，您上传或复制具有相同键名称的对象，则该对象将被覆盖。有关使用键名称的更多信息，请参阅[为 Amazon S3 对象命名](object-keys.md)。

**注意**  
HTTP `If-None-Match` 标头仅适用于有版本控制的存储桶中对象的当前版本。

要执行具有 HTTP `If-None-Match` 标头的有条件写入，您必须拥有 `s3:PutObject` 权限。这使调用方能够检查存储桶中是否存在对象。`If-None-Match` 标头需要 \$1（星号）值。

您可以将 `If-None-Match` 标头与以下 API 结合使用：
+ [https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html)
+ [https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html)
+ [https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html)

### 使用 AWS CLI 进行有条件放置
<a name="conditional-writes-putobject-CLI-key-names"></a>

以下 `put-object` 示例命令尝试对键名称为 `dir-1/my_images.tar.bz2` 的对象执行有条件写入。

```
aws s3api put-object --bucket amzn-s3-demo-bucket --key dir-1/my_images.tar.bz2 --body my_images.tar.bz2 --if-none-match "*"       
```

有关更多信息，请参阅《AWS CLI Command Reference》**中的 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-object.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-object.html)。

有关 AWS CLI 的更多信息，请参阅《*AWS Command Line Interface 用户指南*》中的[什么是 AWS Command Line Interface？](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html)。

### 使用 AWS CLI 进行有条件复制
<a name="conditional-writes-copyobject-CLI-key-names"></a>

以下 `copy-object` 示例命令尝试将键名称为 `dir-1/my_images.tar.bz2` 的对象有条件写入到目标存储桶。

```
aws s3api copy-object --copy-source amzn-s3-demo-bucket/key --key dir-1/my_images.tar.bz2 --bucket amzn-s3-demo-bucket2 --if-none-match "*"            
```

有关更多信息，请参阅《AWS CLI Command Reference》**中的 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/copy-object.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/copy-object.html)。

有关 AWS CLI 的更多信息，请参阅《*AWS Command Line Interface 用户指南*》中的[什么是 AWS Command Line Interface？](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html)。

### 使用 AWS CLI 进行有条件分段上传
<a name="conditional-writes-mpu-complete-CLI-key-names"></a>

以下 `complete-multipart-upload` 示例命令尝试对键名称为 `dir-1/my_images.tar.bz2` 的对象使用有条件写入来完成分段上传。在此示例中，file:// 前缀用于从本地文件夹中名为 `mpustruct` 的文件加载 JSON 结构，这将列出此特定分段上传已经上传的所有分段。

```
aws s3api complete-multipart-upload --multipart-upload file://mpustruct --bucket amzn-s3-demo-bucket --key dir-1/my_images.tar.bz2 --upload-id upload-id  --if-none-match "*"             
```

有关更多信息，请参阅《AWS CLI Command Reference》**中的 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/complete-multipart-upload.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/complete-multipart-upload.html)。

有关 AWS CLI 的更多信息，请参阅《*AWS Command Line Interface 用户指南*》中的[什么是 AWS Command Line Interface？](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html)。

## 如果对象已更改，如何防止被覆盖
<a name="conditional-write-etags"></a>

对象的 ETag 是该对象所特有的字符串，它反映了对象内容的变化。可以使用 `If-Match` 标头将 Amazon S3 存储桶中对象的 ETag 值与您在 `WRITE` 操作期间提供的值进行比较。如果 ETag 值不匹配，则操作会失败。有关 ETag 的更多信息，请参阅[使用 Content-MD5 和 ETag 验证上传的对象](checking-object-integrity-upload.md#checking-object-integrity-etag-and-md5)。

要执行具有 HTTP `If-Match` 标头的有条件写入，您必须拥有 `s3:PutObject` 和 `s3:GetObject` 权限。这使调用方能够检查 ETag 并验证存储桶中对象的状态。`If-Match` 标头要求将 ETag 值作为字符串。

您可以将 `If-Match` 标头与以下 API 结合使用：
+ [https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html)
+ [https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html)
+ [https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html)

### 使用 AWS CLI 进行有条件放置
<a name="conditional-writes-putobject-CLI-etags"></a>

以下 `put-object` 示例命令尝试使用提供的 ETag 值 `6805f2cfc46c0f04559748bb039d69ae` 执行有条件写入。

```
aws s3api put-object --bucket amzn-s3-demo-bucket --key dir-1/my_images.tar.bz2 --body my_images.tar.bz2 --if-match "6805f2cfc46c0f04559748bb039d69ae"         
```

有关更多信息，请参阅《AWS CLI Command Reference》**中的 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-object.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/put-object.html)。

有关 AWS CLI 的更多信息，请参阅《*AWS Command Line Interface 用户指南*》中的[什么是 AWS Command Line Interface？](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html)。

### 使用 AWS CLI 进行有条件复制
<a name="conditional-writes-copyobject-CLI-etags"></a>

以下 `copy-object` 示例命令尝试使用提供的 ETag 值 `6805f2cfc46c0f04559748bb039d69ae` 执行有条件写入。

```
aws s3api copy-object --copy-source amzn-s3-demo-bucket/key --key dir-1/my_images.tar.bz2 --bucket amzn-s3-demo-bucket2 --if-match "6805f2cfc46c0f04559748bb039d69ae"             
```

有关更多信息，请参阅《AWS CLI Command Reference》**中的 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/copy-object.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/copy-object.html)。

有关 AWS CLI 的更多信息，请参阅《*AWS Command Line Interface 用户指南*》中的[什么是 AWS Command Line Interface？](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html)。

### 使用 AWS CLI 进行有条件分段上传
<a name="conditional-writes-mpu-complete-CLI-etags"></a>

以下 `complete-multipart-upload` 示例命令尝试使用提供的 ETag 值 `6805f2cfc46c0f04559748bb039d69ae` 通过有条件写入完成分段上传。在此示例中，file:// 前缀用于从本地文件夹中名为 `mpustruct` 的文件加载 JSON 结构，这将列出此特定分段上传已经上传的所有分段。

```
aws s3api complete-multipart-upload --multipart-upload file://mpustruct --bucket amzn-s3-demo-bucket --key dir-1/my_images.tar.bz2 --upload-id upload-id --if-match "6805f2cfc46c0f04559748bb039d69ae"             
```

有关更多信息，请参阅《AWS CLI Command Reference》**中的 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/complete-multipart-upload.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3api/complete-multipart-upload.html)。

有关 AWS CLI 的更多信息，请参阅《*AWS Command Line Interface 用户指南*》中的[什么是 AWS Command Line Interface？](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html)。

## 有条件写入行为
<a name="conditional-error-response"></a>

**具有 `If-None-Match` 标头的有条件写入或复制**  
具有 `If-None-Match` 标头的有条件写入会针对存储桶中的现有对象进行评估。如果存储桶中不存在具有相同键名称的现有对象，则写入操作将成功并导致 `200 OK` 响应。如果存在现有对象，则写入操作将失败，并导致 `412 Precondition Failed` 响应。  
对于启用了版本控制的存储桶，如果没有同名的当前对象版本，或者当前对象版本是删除标记，则写入操作将成功。否则，它会导致写入操作失败和 `412 Precondition Failed` 响应。  
如果对同一个对象名称进行多个有条件写入或复制，则第一个要完成的写入操作将成功。然后，Amazon S3 使后续写入失败并生成 `412 Precondition Failed` 响应。  
如果在针对某个对象的有条件写入操作完成之前，对于该对象的删除请求获得成功，则在并发请求的情况下也可能收到 `409 Conflict` 响应。将有条件写入与 `PutObject` 结合使用时，可能会在收到 `409 Conflict` 错误后重试上传。使用 `CompleteMultipartUpload` 时，必须使用 `CreateMultipartUpload` 重新启动分段上传，以便在收到 `409 Conflict` 错误后再次上传对象。

**具有 `If-Match` 标头的有条件写入或复制**  
`If-Match` 标头会针对存储桶中的现有对象进行评估。如果存在具有相同键名称和匹配 ETag 的现有对象，则写入操作将成功并导致 `200 OK` 响应。如果 ETag 不匹配，则写入操作将失败并导致 `412 Precondition Failed` 响应。  
如果是并发请求，您也可能收到 `409 Conflict` 响应。  
如果在针对对象的有条件写入操作完成之前，对于对象的并发删除请求获得成功，您将收到 `404 Not Found` 响应，因为对象键不再存在。收到 `404 Not Found` 响应后，您应该重新上传对象。  
如果没有同名的当前对象版本，或者当前对象版本是删除标记，则该操作失败并出现 `404 Not Found` 错误。

## 有条件写入场景
<a name="conditional-write-scenarios"></a>

考虑以下场景，即两个客户端对同一个存储桶运行操作。

**分段上传期间的有条件写入**  
有条件写入不考虑任何正在进行的分段上传请求，因为这些对象还不是完全写入的对象。请考虑以下示例，其中客户端 1 正在使用分段上传来上传对象。在分段上传期间，客户端 2 能够通过有条件写入操作成功写入相同的对象。随后，当客户端 1 尝试使用有条件写入完成分段上传时，上传将失败。

**注意**  
这种情况将导致 `If-None-Match` 和 `If-Match` 标头都得到 `412 Precondition Failed` 响应。

![\[两个客户端写入具有相同键名称的项目的示例。一个客户端使用适用于 MPU 的 UploadPart，另一个客户端使用 PutObject 和有条件写入。之后启动的 CompleteMultipartUpload 操作会失败。\]](http://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/images/conwrite_put_mpu.png)


**分段上传期间的并发删除**  
如果在有条件写入请求可以完成之前，删除请求获得成功，则 Amazon S3 为写入操作返回 `409 Conflict` 或 `404 Not Found` 响应。这是因为更早启动的删除请求优先于有条件写入操作。在这种情况下，您必须启动新的分段上传。

**注意**  
这种情况将对 `If-None-Match` 标头导致 `409 Conflict` 响应和对 `If-Match` 标头导致 `404 Not Found` 响应。

![\[两个客户端的示例，一个客户端使用分段上传，另一个客户端在 MPU 启动后发送删除请求。删除请求在有条件写入开始之前完成。\]](http://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/images/conwrite_delete_mpu.png)


**注意**  
为了最大程度地降低存储成本，我们建议您配置生命周期规则，以便使用 `AbortIncompleteMultipartUpload` 操作在指定的天数后删除未完成的分段上传。有关创建生命周期规则以删除未完成的分段上传的更多信息，请参阅[配置存储桶生命周期配置以删除未完成的分段上传](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpu-abort-incomplete-mpu-lifecycle-config.html)。

# 强制对 Amazon S3 存储桶实施有条件写入
<a name="conditional-writes-enforce"></a>

通过使用 Amazon S3 存储桶策略，可以对通用存储桶中的对象上传强制实施有条件写入。

存储桶策略是基于资源的策略，您可以使用该策略向 Amazon S3 存储桶及其中的对象授予访问权限。只有存储桶拥有者才能将策略与存储桶关联。有关存储桶策略的更多信息，请参阅 [Amazon S3 的存储桶策略](bucket-policies.md)。

您可以使用条件键 `s3:if-match` 或 `s3:if-none-match` 作为可选的 `Condition` 元素或 `Condition` 块来指定策略何时生效。对于分段上传，您必须指定 `s3:ObjectCreationOperation` 条件键以豁免 `CreateMultipartUpload`、`UploadPart` 和 `UploadPartCopy` 操作，因为这些 API 不接受有条件标头。有关在存储桶策略中使用条件的更多信息，请参阅[使用条件键的存储桶策略示例](amazon-s3-policy-keys.md)。

**注意**  
如果您使用存储桶策略来强制实施有条件写入，则无法对在存储桶策略中指定的存储桶或前缀执行复制操作。不带 `If-None-Match` 或 `If-Match` HTTP 标头的 `CopyObject` 请求失败并出现 `403 Access Denied` 错误。使用这些 HTTP 标头发出的 `CopyObject` 请求失败并导致 `501 Not Implemented` 响应。

以下示例说明如何使用存储桶策略中的条件来强制客户端使用 `If-None-Match` 或 `If-Match` HTTP 标头。

**Topics**
+ [示例 1：仅支持使用包含 `if-none-match` 标头的 `PutObject` 和 `CompleteMultipartUpload` 请求上传对象](#conditional-writes-enforce-ex1)
+ [示例 2：仅支持使用包含 `if-match` 标头的 `PutObject` 和 `CompleteMultipartUpload` 请求上传对象](#conditional-writes-enforce-ex2)
+ [示例 3：仅支持包含 `if-none-match` 或 `if-match` 标头的对象上传请求](#conditional-writes-enforce-ex3)

## 示例 1：仅支持使用包含 `if-none-match` 标头的 `PutObject` 和 `CompleteMultipartUpload` 请求上传对象
<a name="conditional-writes-enforce-ex1"></a>

如果请求包含 `if-none-match` 标头，则此策略支持账户 111122223333（用户 Alice）写入 *amzn-s3-demo-bucket1* 存储桶，从而确保存储桶中不存在对象键。针对指定存储桶的所有 `PutObject` 和 `CompleteMultipartUpload` 请求都必须包含 `if-none-match` 标头才能获得成功。使用此标头，仅当存储桶中不存在对象键时，客户才能写入该存储桶。

**注意**  
该策略还设置了 `s3:ObjectCreationOperation` 条件键，以支持使用 `CreateMultipartUpload`、`UploadPart` 和 `UploadPartCopy` API 进行分段上传。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "AllowConditionalPut",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:user/Alice"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::amzn-s3-demo-bucket1/*",
            "Condition": {
                "Null": {
                    "s3:if-none-match": "false"
                }
            }
        },
        {
            "Sid": "AllowConditionalPutwithMPUs",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:user/Alice"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::amzn-s3-demo-bucket1/*",
            "Condition": {
                "Bool": {
                    "s3:ObjectCreationOperation": "false"
                }
            }
        }
    ]
}
```

------

## 示例 2：仅支持使用包含 `if-match` 标头的 `PutObject` 和 `CompleteMultipartUpload` 请求上传对象
<a name="conditional-writes-enforce-ex2"></a>

只有在请求包含 `if-match` 标头的情况下，此策略才支持账户 111122223333（用户 Alice）写入 *amzn-s3-demo-bucket1*。此标头将 S3 中对象的 ETag 值与您在 `WRITE` 操作期间提供的值进行比较。如果 ETag 值不匹配，则操作将失败。针对指定存储桶的所有 `PutObject` 和 `CompleteMultipartUpload` 请求都必须包含 `if-match` 标头才能获得成功。

**注意**  
该策略还设置了 `s3:ObjectCreationOperation` 条件键，以支持使用 `CreateMultipartUpload`、`UploadPart` 和 `UploadPartCopy` API 进行分段上传。

```
{
    "Version": "2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "AllowPutObject",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:user/Alice"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::amzn-s3-demo-bucket1/*",
        },
        {
            "Sid": "BlockNonConditionalObjectCreation",
            "Effect": "Deny",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:user/Alice"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::amzn-s3-demo-bucket1/*",
            "Condition": {
                "Null": {
                    "s3:if-match": "true"
                },
                "Bool": {
                    "s3:ObjectCreationOperation": "true"
                }
            }
        },
        {
            "Sid": "AllowGetObjectBecauseConditionalPutIfMatchETag",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:user/Alice"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::amzn-s3-demo-bucket1/*"
        }
    ]
}
```

## 示例 3：仅支持包含 `if-none-match` 或 `if-match` 标头的对象上传请求
<a name="conditional-writes-enforce-ex3"></a>

如果请求包含 `if-none-match` 或 `if-match` 标头，则此策略支持账户 111122223333（用户 Alice）写入 *amzn-s3-demo-bucket1*。这可让 Alice 在存储桶中不存在键名称的情况下上传对象，或者，如果键名称确实存在，当对象 ETag 与 `PUT` 请求中提供的 ETag 匹配时，Alice 可以覆盖该对象。

**注意**  
该策略还设置了 `s3:ObjectCreationOperation` 条件键，以支持使用 `CreateMultipartUpload`、`UploadPart` 和 `UploadPartCopy` API 进行分段上传。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": " AllowConditionalPutifAbsent",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:user/Alice"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::amzn-s3-demo-bucket1/*",
            "Condition": {
                "Null": {
                    "s3:if-none-match": "false"
                }
            }
        },
        {
            "Sid": "AllowConditionalPutIfMatchEtag",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:user/Alice"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::amzn-s3-demo-bucket1/*",
            "Condition": {
                "Null": {
                    "s3:if-match": "false"
                }
            }
        },
        {
            "Sid": "AllowConditionalObjectCreation",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:user/Alice"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::amzn-s3-demo-bucket1/*",
            "Condition": {
                "Bool": {
                    "s3:ObjectCreationOperation": "false"
                }
            }
        },
        {
            "Sid": " AllowGetObjectBecauseConditionalPutIfMatchETag",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:user/Alice"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::amzn-s3-demo-bucket1/*"
        }
    ]
}
```

------