本文介紹如何在受版本控制的儲存空間(Bucket)中拷貝檔案(Object)。您可以通過CopyObject的方法拷貝小於1 GB的檔案,通過分區拷貝(UploadPartCopy)的方法拷貝大於1 GB的檔案。
注意事項
本文以華東1(杭州)外網Endpoint為例。如果您希望通過與OSS同地區的其他阿里雲產品訪問OSS,請使用內網Endpoint。關於OSS支援的Region與Endpoint的對應關係,請參見地區和Endpoint。
本文以OSS網域名稱建立OSSClient為例。如果您希望通過自訂網域名、STS等方式建立OSSClient,請參見建立OssClient。
要拷貝檔案,您必須有
oss:GetObject和oss:PutObject許可權。具體操作,請參見為RAM使用者授予自訂的權限原則。
拷貝小檔案
對於小於1 GB的檔案,您可以通過CopyObject方法將檔案從源Bucket複製到同一地區的目標Bucket。
x-oss-copy-source預設拷貝Object的目前的版本。如果目前的版本是刪除標記,則返回404表示該Object不存在。您可以在x-oss-copy-source中加入versionId來拷貝指定版本的Object,刪除標記不能被拷貝。
您可以將Object的早期版本拷貝到同一個Bucket中,拷貝Object的歷史版本將會成為一個新的目前的版本,達到恢複Object早期版本的目的。
如果目標Bucket已開啟版本控制,OSS將會為新拷貝的Object自動產生唯一的版本ID,此版本ID將會在響應Header的x-oss-version-id中返回。如果目標Bucket未曾開啟或者暫停了版本控制,OSS將會為新拷貝的Object自動產生version ID為”null“的版本,且會覆蓋原versionId為”null“的版本。
目標Bucket在開啟或暫停版本控制狀態下,不支援對Appendable類型Object執行拷貝操作。
以下代碼用於拷貝小檔案:
<?php
if (is_file(__DIR__ . '/../autoload.php')) {
require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
}
use OSS\Credentials\EnvironmentVariableCredentialsProvider;
use OSS\OssClient;
use OSS\CoreOssException;
// 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
$provider = new EnvironmentVariableCredentialsProvider();
// Endpoint以杭州為例,其它Region請按實際情況填寫。
$endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
$bucket= "<yourBucketName>";
// 填寫不包含Bucket名稱在內的Object的完整路徑,例如example/test.txt。
$object = "<yourObjectName>";
$versionId = "<yourObjectVersionId>";
$to_bucket = $bucket;
$to_object = $object . '.copy';
$config = array(
"provider" => $provider,
"endpoint" => $endpoint,
"signatureVersion" => OssClient::OSS_SIGNATURE_VERSION_V4,
"region"=> "cn-hangzhou"
);
$ossClient = new OssClient($config);
try{
// 拷貝指定版本的Object。
$ret = $ossClient->copyObject($bucket, $object, $to_bucket, $to_object, array(OssClient::OSS_VERSION_ID => $versionId));
print("versionId:" .$ret['x-oss-version-id']);
} catch(OssException $e) {
printf(__FUNCTION__ . ": FAILED\n");
printf($e->getMessage() . "\n");
return;
}
print(__FUNCTION__ . ": OK" . "\n");
拷貝小檔案的詳細說明請參見CopyObject。
拷貝大檔案
對於大於1 GB的檔案,需要使用分區拷貝(UploadPartCopy)。
UploadPartCopy預設從一個已存在的Object的目前的版本中拷貝資料來上傳一個Part。允許通過在x-oss-copy-source的請求Header中附帶versionId的子條件,實現從Object的指定版本進行拷貝,例如x-oss-copy-source : /SourceBucketName/SourceObjectName?versionId=111111。
SourceObjectName要進行URL編碼。響應中將會返回被拷貝的Object版本ID:x-oss-copy-source-version-id。
如果未指定versionId且拷貝Object的目前的版本為刪除標記,OSS將返回404 Not Found。通過指定versionId來拷貝刪除標記時,OSS將返回400 Bad Request。
以下代碼用於分區拷貝:
<?php
if (is_file(__DIR__ . '/../autoload.php')) {
require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
}
use OSS\Credentials\EnvironmentVariableCredentialsProvider;
use OSS\OssClient;
use OSS\CoreOssException;
// 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
$provider = new EnvironmentVariableCredentialsProvider();
// Endpoint以杭州為例,其它Region請按實際情況填寫。
$endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
$bucket= "<yourBucketName>";
$object = "<yourObjectName>";
$versionId = "<yourObjectVersionId>";
$to_bucket = $bucket;
$to_object = $object . '.copy';
$config = array(
"provider" => $provider,
"endpoint" => $endpoint,
"signatureVersion" => OssClient::OSS_SIGNATURE_VERSION_V4,
"region"=> "cn-hangzhou"
);
$ossClient = new OssClient($config);
/**
* 步驟1:初始化一個分區上傳事件,擷取uploadId。
*/
try {
// 返回uploadId。uploadId是分區上傳事件的唯一標識,您可以根據uploadId發起相關的操作,如取消分區上傳、查詢分區上傳等。
$uploadId = $ossClient->initiateMultipartUpload($to_bucket, $to_object);
} catch(OssException $e) {
printf(__FUNCTION__ . ": initiateMultipartUpload FAILED\n");
printf($e->getMessage() . "\n");
return;
}
print(__FUNCTION__ . ": initiateMultipartUpload OK" . "\n");
/*
* 步驟2:上傳分區。
*/
$partSize = 10 * 1024 * 1024;
try {
$meta = $ossClient->getSimplifiedObjectMeta($bucket, $object);
$uploadSize = $meta[strtolower('Content-Length')];
} catch(OssException $e) {
printf(__FUNCTION__ . ": getSimplifiedObjectMeta FAILED\n");
printf($e->getMessage() . "\n");
return;
}
$pieces = $ossClient->generateMultiuploadParts($uploadSize, $partSize);
$responseUploadPart = array();
$uploadPosition = 0;
foreach ($pieces as $i => $piece) {
$fromPos = $uploadPosition + (integer)$piece[$ossClient::OSS_SEEK_TO];
$toPos = (integer)$piece[$ossClient::OSS_LENGTH] + $fromPos - 1;
$upOptions = array(
// 指定源Object的起始位置。
'start' => $fromPos,
// 指定源Object的結束位置。
'end' => $toPos,
// 指定源Object的版本ID。
OssClient::OSS_VERSION_ID => $versionId
);
try {
// 上傳分區。
$responseUploadPart[] = $ossClient->uploadPartCopy($bucket, $object, $to_bucket, $to_object, ($i + 1), $uploadId, $upOptions);
} catch(OssException $e) {
printf(__FUNCTION__ . ": initiateMultipartUpload, uploadPartCopy - part#{$i} FAILED\n");
printf($e->getMessage() . "\n");
return;
}
printf(__FUNCTION__ . ": initiateMultipartUpload, uploadPartCopy - part#{$i} OK\n");
}
// $uploadParts是由每個分區的ETag和分區號(PartNumber)組成的數組。
$uploadParts = array();
foreach ($responseUploadPart as $i => $eTag) {
$uploadParts[] = array(
'PartNumber' => ($i + 1),
'ETag' => $eTag,
);
}
/**
* 步驟3:完成分區上傳。
*/
try {
// 執行completeMultipartUpload操作時,需要提供所有有效$uploadParts。OSS收到提交的$uploadParts後,會逐一驗證每個分區的有效性。當所有的資料分區驗證通過後,OSS將把這些分區組合成一個完整的檔案。
$ret = $ossClient->completeMultipartUpload($to_bucket, $to_object, $uploadId, $uploadParts);
// 查看Object的版本資訊。
print("versionId:" .$ret[OssClient::OSS_HEADER_VERSION_ID]);
} catch(OssException $e) {
printf(__FUNCTION__ . ": completeMultipartUpload FAILED\n");
printf($e->getMessage() . "\n");
return;
}
printf(__FUNCTION__ . ": completeMultipartUpload OK\n"); 分區拷貝的詳細資料請參見UploadPartCopy。