オブジェクトのコンテンツを変更せずに、同じリージョン内のソースバケットから送信先バケットへオブジェクトをコピーします。
CopyObject 操作を使用すると、次のことができます。
同じリージョン内の別のバケットにオブジェクトをバックアップします。
ダウンロードと再アップロードなしで、バケット間でオブジェクトを移行します。
同じキーにコピーすることで、オブジェクトのストレージクラス、アクセス制御リスト (ACL)、メタデータ、またはタグを変更します。
前提条件
オブジェクトをコピーする前に、以下を確認してください。
ソースオブジェクトに対する読み取り権限
送信先バケットに対する読み書き権限
いずれかの権限が不足している場合、コピー操作は失敗します。
制限事項
クロスリージョンコピーはサポートされていません。たとえば、中国 (杭州) と中国 (上海) の間でオブジェクトをコピーすることはできません。
1 GB を超えるオブジェクトは、マルチパートコピー方式 (UploadPartCopy) を使用する必要があります。ossbrowser は 5 GB までのオブジェクトをサポートします。
ソースバケットまたは送信先バケットに保持ポリシーが設定されている場合、コピー操作はエラー「
指定されたオブジェクトは変更できません」を返します。
コピー方法の選択
| メソッド | オブジェクトサイズ | 備考 |
|---|---|---|
| ossbrowser | 5 GB まで | GUI ツール。コード不要 |
| OSS SDK (CopyObject) | 1 GB まで | プログラムによるアクセスに推奨 |
| OSS SDK (UploadPartCopy) | 1 GB を超える | マルチパートコピー。「概要 |
| ossutil | 任意のサイズ | CLI ツール |
| CopyObject API | 1 回の呼び出しにつき 1 GB まで | 直接 API 呼び出し。署名計算が必要 |
上書き動作
デフォルトでは、送信先バケット内の既存のオブジェクトと同じキーを持つオブジェクトをコピーすると、そのオブジェクトは上書きされます。意図しない上書きを防ぐには、次のいずれかの方法を使用します。
送信先バケットでバージョン管理を有効にします。 上書きされたオブジェクトは以前のバージョンとして保存され、いつでも回復できます。詳細については、「バージョン管理」をご参照ください。
`x-oss-forbid-overwrite` ヘッダーを `true` に設定します。 送信先キーが既に存在する場合、コピーは失敗し、OSS は
FileAlreadyExistsエラーコードを返します。
コストに関する考慮事項
多数のオブジェクトをコピーし、すぐにストレージクラスをディープコールドアーカイブストレージに設定すると、高額な PUT リクエスト料金が発生する可能性があります。コストを削減するには、コピー後にオブジェクトをディープコールドアーカイブストレージにトランジションするようにライフサイクルルールを設定します。
オブジェクトのコピー
OSS SDK の使用
次の例では、CopyObject 操作を使用して 1 GB 未満のオブジェクトをコピーします。1 GB を超えるオブジェクトの場合は、UploadPartCopy を使用します。「概要」をご参照ください。
すべての例は、環境変数 (OSS_ACCESS_KEY_ID および OSS_ACCESS_KEY_SECRET) から認証情報を読み取ります。コードを実行する前に、これらの変数を設定してください。
コピー時のメタデータ動作
デフォルトでは、OSS はソースオブジェクトのメタデータを送信先オブジェクトにコピーします (COPY モード)。メタデータを置き換えるには、x-oss-metadata-directive を REPLACE に設定し、リクエストで新しいメタデータ値を指定します。メタデータを置き換える場合、すべてのメタデータフィールドを明示的に宣言する必要があります。フィールドを省略すると、送信先オブジェクトから削除されます。
<details> <summary>Python</summary>
import argparse
import alibabacloud_oss_v2 as oss
parser = argparse.ArgumentParser(description="copy object sample")
parser.add_argument('--region', required=True, help='The region in which the bucket is located.')
parser.add_argument('--bucket', required=True, help='The name of the destination bucket.')
parser.add_argument('--endpoint', help='The endpoint of the region.')
parser.add_argument('--key', required=True, help='The key of the destination object.')
parser.add_argument('--source_key', required=True, help='The key of the source object.')
parser.add_argument('--source_bucket', required=True, help='The name of the source bucket.')
def main():
args = parser.parse_args()
credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
cfg = oss.config.load_default()
cfg.credentials_provider = credentials_provider
cfg.region = args.region
if args.endpoint is not None:
cfg.endpoint = args.endpoint
client = oss.Client(cfg)
result = client.copy_object(oss.CopyObjectRequest(
bucket=args.bucket,
key=args.key,
source_key=args.source_key,
source_bucket=args.source_bucket,
))
print(f'status code: {result.status_code},'
f' request id: {result.request_id},'
f' version id: {result.version_id},'
f' hash crc64: {result.hash_crc64},'
f' source version id: {result.source_version_id},'
f' server side encryption: {result.server_side_encryption},'
f' server side data encryption: {result.server_side_data_encryption},'
f' last modified: {result.last_modified},'
f' etag: {result.etag}')
if __name__ == "__main__":
main()</details>
<details> <summary>Java</summary>
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.*;
public class Demo {
public static void main(String[] args) throws Exception {
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // ご利用のエンドポイントに置き換えてください
String region = "cn-hangzhou"; // ご利用のリージョンに置き換えてください
String sourceBucketName = "srcexamplebucket";
String sourceKey = "srcexampleobject.txt";
String destinationBucketName = "desexamplebucket"; // 同じリージョンである必要があります
String destinationKey = "desexampleobject.txt";
EnvironmentVariableCredentialsProvider credentialsProvider =
CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
try {
CopyObjectRequest copyObjectRequest = new CopyObjectRequest(
sourceBucketName, sourceKey,
destinationBucketName, destinationKey
);
CopyObjectResult result = ossClient.copyObject(copyObjectRequest);
System.out.println("ETag: " + result.getETag() + " LastModified: " + result.getLastModified());
} catch (OSSException oe) {
System.out.println("Error Code: " + oe.getErrorCode());
System.out.println("Request ID: " + oe.getRequestId());
} catch (ClientException ce) {
System.out.println("Client Error: " + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}</details>
<details> <summary>Go</summary>
package main
import (
"context"
"flag"
"log"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
)
var (
region string
srcBucketName string
srcObjectName string
destBucketName string
destObjectName string
)
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&srcBucketName, "src-bucket", "", "The name of the source bucket.")
flag.StringVar(&srcObjectName, "src-object", "", "The name of the source object.")
flag.StringVar(&destBucketName, "dest-bucket", "", "The name of the destination bucket.")
flag.StringVar(&destObjectName, "dest-object", "", "The name of the destination object.")
}
func main() {
flag.Parse()
if len(srcBucketName) == 0 {
log.Fatalf("invalid parameters, source bucket name required")
}
if len(region) == 0 {
log.Fatalf("invalid parameters, region required")
}
if len(destBucketName) == 0 {
destBucketName = srcBucketName // Default to source bucket for same-bucket copy
}
if len(srcObjectName) == 0 {
log.Fatalf("invalid parameters, source object name required")
}
if len(destObjectName) == 0 {
log.Fatalf("invalid parameters, destination object name required")
}
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
client := oss.NewClient(cfg)
request := &oss.CopyObjectRequest{
Bucket: oss.Ptr(destBucketName),
Key: oss.Ptr(destObjectName),
SourceKey: oss.Ptr(srcObjectName),
SourceBucket: oss.Ptr(srcBucketName),
}
result, err := client.CopyObject(context.TODO(), request)
if err != nil {
log.Fatalf("failed to copy object %v", err)
}
log.Printf("copy object result: %#v\n", result)
}</details>
<details> <summary>PHP</summary>
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use AlibabaCloud\Oss\V2 as Oss;
$optsdesc = [
"region" => ['help' => 'The region in which the bucket is located.', 'required' => true],
"endpoint" => ['help' => 'The endpoint of the region.', 'required' => false],
"bucket" => ['help' => 'The name of the destination bucket.', 'required' => true],
"key" => ['help' => 'The key of the destination object.', 'required' => true],
"src-bucket" => ['help' => 'The name of the source bucket.', 'required' => false],
"src-key" => ['help' => 'The key of the source object.', 'required' => true],
];
$longopts = array_map(fn($key) => "$key:", array_keys($optsdesc));
$options = getopt("", $longopts);
foreach ($optsdesc as $key => $value) {
if ($value['required'] === true && empty($options[$key])) {
echo "Error: --$key is required. " . $value['help'] . PHP_EOL;
exit(1);
}
}
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();
$cfg = Oss\Config::loadDefault();
$cfg->setCredentialsProvider($credentialsProvider);
$cfg->setRegion($options["region"]);
if (isset($options["endpoint"])) {
$cfg->setEndpoint($options["endpoint"]);
}
$client = new Oss\Client($cfg);
$request = new Oss\Models\CopyObjectRequest(
bucket: $options["bucket"],
key: $options["key"]
);
if (!empty($options["src-bucket"])) {
$request->sourceBucket = $options["src-bucket"];
}
$request->sourceKey = $options["src-key"];
$result = $client->copyObject($request);
printf(
'status code: %s' . PHP_EOL .
'request id: %s' . PHP_EOL,
$result->statusCode,
$result->requestId
);</details>
<details> <summary>Node.js</summary>
const OSS = require('ali-oss');
const client = new OSS({
region: 'yourRegion', // ご利用のリージョンに置き換えてください
accessKeyId: process.env.OSS_ACCESS_KEY_ID,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
authorizationV4: true,
bucket: 'examplebucket',
});
async function copyObject() {
try {
const result = await client.copy('destexampleobject.txt', 'srcexampleobject.txt');
console.log(result);
} catch (e) {
console.log(e);
}
}
copyObject();</details>
<details> <summary>C#</summary>
using Aliyun.OSS;
using Aliyun.OSS.Common;
var endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // ご利用のエンドポイントに置き換えてください
var region = "cn-hangzhou"; // ご利用のリージョンに置き換えてください
var accessKeyId = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_ID");
var accessKeySecret = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_SECRET");
var sourceBucket = "srcexamplebucket";
var sourceObject = "srcdir/srcobject.txt";
var targetBucket = "destbucket"; // 同じリージョンである必要があります
var targetObject = "destdir/destobject.txt";
var conf = new ClientConfiguration();
conf.SignatureVersion = SignatureVersion.V4;
var client = new OssClient(endpoint, accessKeyId, accessKeySecret, conf);
client.SetRegion(region);
try
{
var req = new CopyObjectRequest(sourceBucket, sourceObject, targetBucket, targetObject);
client.CopyObject(req);
Console.WriteLine("Copy object succeeded");
}
catch (OssException ex)
{
Console.WriteLine("Failed with error code: {0}; Error info: {1}. \nRequestID: {2} \tHostID: {3}",
ex.ErrorCode, ex.Message, ex.RequestId, ex.HostId);
}
catch (Exception ex)
{
Console.WriteLine("Failed with error info: {0}", ex.Message);
}</details>
<details> <summary>Ruby</summary>
require 'aliyun/oss'
client = Aliyun::OSS::Client.new(
endpoint: 'https://oss-cn-hangzhou.aliyuncs.com', # ご利用のエンドポイントに置き換えてください
access_key_id: ENV['OSS_ACCESS_KEY_ID'],
access_key_secret: ENV['OSS_ACCESS_KEY_SECRET']
)
bucket = client.get_bucket('examplebucket')
# オブジェクトとそのメタデータをコピーします
bucket.copy_object('destobject.txt', 'srcobject.txt',
:meta_directive => Aliyun::OSS::MetaDirective::COPY)
# オブジェクトをコピーし、そのメタデータを置き換えます
bucket.copy_object('destobject.txt', 'srcobject.txt',
:metas => {'year' => '2017'},
:meta_directive => Aliyun::OSS::MetaDirective::REPLACE)</details>
<details> <summary>Android (Java)</summary>
String srcBucketName = "srcbucket";
String srcObjectKey = "dir1/srcobject.txt";
String destBucketName = "destbucket"; // 同じリージョンである必要があります
String destObjectKey = "dir2/destobject.txt";
CopyObjectRequest copyObjectRequest = new CopyObjectRequest(
srcBucketName, srcObjectKey,
destBucketName, destObjectKey
);
OSSAsyncTask copyTask = oss.asyncCopyObject(copyObjectRequest,
new OSSCompletedCallback<CopyObjectRequest, CopyObjectResult>() {
@Override
public void onSuccess(CopyObjectRequest request, CopyObjectResult result) {
Log.d("copyObject", "copy success!");
}
@Override
public void onFailure(CopyObjectRequest request,
ClientException clientException,
ServiceException serviceException) {
if (clientException != null) {
clientException.printStackTrace();
}
if (serviceException != null) {
Log.e("ErrorCode", serviceException.getErrorCode());
Log.e("RequestId", serviceException.getRequestId());
Log.e("HostId", serviceException.getHostId());
Log.e("RawMessage", serviceException.getRawMessage());
}
}
});</details>
<details> <summary>iOS (Objective-C)</summary>
OSSCopyObjectRequest *copy = [OSSCopyObjectRequest new];
copy.sourceBucketName = @"sourcebucket";
copy.sourceObjectKey = @"dir1/srcobject.txt";
copy.bucketName = @"destbucket";
copy.objectKey = @"dir2/destobject.txt";
OSSTask *task = [client copyObject:copy];
[task continueWithBlock:^id(OSSTask *task) {
if (!task.error) {
NSLog(@"copy object success!");
} else {
NSLog(@"copy object failed, error: %@", task.error);
}
return nil;
}];</details>
<details> <summary>C++</summary>
#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;
int main(void)
{
std::string Endpoint = "yourEndpoint"; // ご利用のエンドポイントに置き換えてください
std::string Region = "yourRegion"; // ご利用のリージョンに置き換えてください
std::string SourceBucketName = "srcexamplebucket";
std::string CopyBucketName = "destbucket"; // 同じリージョンである必要があります
std::string SourceObjectName = "srcdir/srcobject.txt";
std::string CopyObjectName = "destdir/destobject.txt";
InitializeSdk();
ClientConfiguration conf;
conf.signatureVersion = SignatureVersionType::V4;
auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
OssClient client(Endpoint, credentialsProvider, conf);
client.SetRegion(Region);
CopyObjectRequest request(CopyBucketName, CopyObjectName);
request.setCopySource(SourceBucketName, SourceObjectName);
auto outcome = client.CopyObject(request);
if (!outcome.isSuccess()) {
std::cout << "CopyObject failed"
<< ", code: " << outcome.error().Code()
<< ", message: " << outcome.error().Message()
<< ", requestId: " << outcome.error().RequestId() << std::endl;
ShutdownSdk();
return -1;
}
ShutdownSdk();
return 0;
}</details>
<details> <summary>C</summary>
#include "oss_api.h"
#include "aos_http_io.h"
const char *endpoint = "yourEndpoint"; // ご利用のエンドポイントに置き換えてください
const char *region = "yourRegion"; // ご利用のリージョンに置き換えてください
const char *source_bucket_name = "yourSourceBucketName";
const char *source_object_name = "yourSourceObjectName";
const char *dest_bucket_name = "yourDestBucketName"; // 同じリージョンである必要があります
const char *dest_object_name = "yourDestObjectName";
void init_options(oss_request_options_t *options)
{
options->config = oss_config_create(options->pool);
aos_str_set(&options->config->endpoint, endpoint);
aos_str_set(&options->config->access_key_id, getenv("OSS_ACCESS_KEY_ID"));
aos_str_set(&options->config->access_key_secret, getenv("OSS_ACCESS_KEY_SECRET"));
aos_str_set(&options->config->region, region);
options->config->signature_version = 4;
options->config->is_cname = 0;
options->ctl = aos_http_controller_create(options->pool, 0);
}
int main(int argc, char *argv[])
{
if (aos_http_io_initialize(NULL, 0) != AOSE_OK) {
exit(1);
}
aos_pool_t *pool;
aos_pool_create(&pool, NULL);
oss_request_options_t *oss_client_options = oss_request_options_create(pool);
init_options(oss_client_options);
aos_string_t source_bucket, source_object, dest_bucket, dest_object;
aos_table_t *headers = NULL, *resp_headers = NULL;
aos_status_t *resp_status = NULL;
aos_str_set(&source_bucket, source_bucket_name);
aos_str_set(&source_object, source_object_name);
aos_str_set(&dest_bucket, dest_bucket_name);
aos_str_set(&dest_object, dest_object_name);
headers = aos_table_make(pool, 0);
resp_status = oss_copy_object(oss_client_options,
&source_bucket, &source_object,
&dest_bucket, &dest_object,
headers, &resp_headers);
if (aos_status_is_ok(resp_status)) {
printf("copy object succeeded\n");
} else {
printf("copy object failed\n");
}
aos_pool_destroy(pool);
aos_http_io_deinitialize();
return 0;
}</details>
ossutil の使用
CopyObject API を直接使用
上記の方法はすべて、CopyObject API オペレーションに基づいています。テストケースでカスタムリクエストの構築が必要な場合は、API を直接呼び出します。直接の API 呼び出しでは、コード内で署名計算が必要です。
次のステップ
1 GB を超えるオブジェクトをコピーするには、UploadPartCopy 操作を使用します。「概要」をご参照ください。
意図しない上書きからオブジェクトを保護するには、送信先バケットでバージョン管理を有効にします。
コピー後にストレージコストを管理するには、オブジェクトを低コストのストレージクラスに自動的にトランジションするようにライフサイクルルールを設定します。