本文介紹如何將源Bucket中的檔案(Object)複製到同一地區下相同或不同目標Bucket中。
注意事項
本文以華東1(杭州)外網Endpoint為例。如果您希望通過與OSS同地區的其他阿里雲產品訪問OSS,請使用內網Endpoint。關於OSS支援的Region與Endpoint的對應關係,請參見OSS訪問網域名稱、資料中心、開放連接埠。
本文以從環境變數讀取存取憑證為例。如何配置訪問憑證,請參見Java配置訪問憑證。
本文以OSS網域名稱建立OSSClient為例。如果您希望通過自訂網域名、STS等方式建立OSSClient,請參見建立OSSClient。
拷貝檔案時,您必須擁有源檔案的讀許可權及目標Bucket的讀寫權限。
拷貝檔案時,您需要確保源Bucket和目標Bucket均未設定合規保留原則,否則報錯The object you specified is immutable.。
不支援跨地區拷貝。例如不能將華東1(杭州)地區儲存空間中的檔案拷貝到華北1(青島)地區。
拷貝小檔案
對於小於1 GB的檔案,您可以通過ossClient.copyObject方法拷貝檔案。此方法有兩種參數指定方式:
參數指定方式 | 描述 |
CopyObjectResult copyObject(String sourceBucketName, String sourceKey, String destinationBucketName, String destinationKey) | 允許指定源儲存空間和源檔案,以及目標儲存空間和目標檔案。拷貝後,目標檔案的內容及中繼資料與源檔案相同,稱為簡單拷貝。 |
CopyObjectResult copyObject(CopyObjectRequest copyObjectRequest) | 允許指定目標檔案的中繼資料和拷貝的限制條件。如果拷貝操作的源檔案地址和目標檔案地址相同,則直接替換源檔案的中繼資料。 |
CopyObjectRequest可設定的參數請參見下表。
參數 | 描述 | 方法 |
sourceBucketName | 源儲存空間名稱。 | setSourceBucketName(String sourceBucketName) |
sourceKey | 源檔案名稱。 | setSourceKey(String sourceKey) |
destinationBucketName | 目標儲存空間名稱。 | setDestinationBucketName(String destinationBucketName) |
destinationKey | 目標檔案名稱。 | setDestinationKey(String destinationKey) |
newObjectMetadata | 目標檔案中繼資料。 | setNewObjectMetadata(ObjectMetadata newObjectMetadata) |
matchingETagConstraints | 拷貝的限制條件。如果源檔案的ETag值和使用者提供的ETag值相等,則執行拷貝操作,否則返回錯誤。 | setMatchingETagConstraints(List<String> matchingETagConstraints) |
nonmatchingEtagConstraints | 拷貝的限制條件。如果源檔案的ETag值和使用者提供的ETag值不相等,則執行拷貝操作,否則返回錯誤。 | setNonmatchingETagConstraints(List<String> nonmatchingEtagConstraints) |
unmodifiedSinceConstraint | 拷貝的限制條件。如果傳入參數中的時間等於或者晚於檔案實際修改時間,則執行拷貝操作,否則返回錯誤。 | setUnmodifiedSinceConstraint(Date unmodifiedSinceConstraint) |
modifiedSinceConstraint | 拷貝的限制條件。如果源檔案在指定的時間之後被修改過,則執行拷貝操作,否則返回錯誤。 | setModifiedSinceConstraint(Date modifiedSinceConstraint) |
CopyObjectResult的參數說明請參見下表。
參數 | 描述 | 方法 |
etag | OSS檔案唯一性標識。 | String getETag() |
lastModified | 檔案最後修改時間。 | Date getLastModified() |
拷貝小檔案有以下幾種拷貝形式。
簡單拷貝
以下代碼用於通過簡單拷貝將srcexamplebucket中的srcexampleobject.txt檔案拷貝到目標儲存空間desexamplebucket中的desexampleobject.txt檔案。
import com.aliyun.oss.ClientException; import com.aliyun.oss.OSS; import com.aliyun.oss.common.auth.*; import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.OSSException; import com.aliyun.oss.model.*; public class Demo { public static void main(String[] args) throws Exception { // Endpoint以華東1(杭州)為例,其它Region請按實際情況填寫。 String endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider(); // 填寫源Bucket名稱。 String sourceBucketName = "srcexamplebucket"; // 填寫源Object的完整路徑,完整路徑中不能包含Bucket名稱。 String sourceKey = "srcexampleobject.txt"; // 填寫與源Bucket處於同一地區的目標Bucket名稱。 String destinationBucketName = "desexamplebucket"; // 填寫目標Object的完整路徑。Object完整路徑中不能包含Bucket名稱。 String destinationKey = "desexampleobject.txt"; // 填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為cn-hangzhou。 String region = "cn-hangzhou"; // 建立OSSClient執行個體。 ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration(); clientBuilderConfiguration.setSignatureVersion(SignVersion.V4); OSS ossClient = OSSClientBuilder.create() .endpoint(endpoint) .credentialsProvider(credentialsProvider) .clientConfiguration(clientBuilderConfiguration) .region(region) .build(); try { // 拷貝檔案。 CopyObjectResult result = ossClient.copyObject(sourceBucketName, sourceKey, destinationBucketName, destinationKey); System.out.println("ETag: " + result.getETag() + " LastModified: " + result.getLastModified()); } catch (OSSException oe) { System.out.println("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason."); System.out.println("Error Message:" + oe.getErrorMessage()); System.out.println("Error Code:" + oe.getErrorCode()); System.out.println("Request ID:" + oe.getRequestId()); System.out.println("Host ID:" + oe.getHostId()); } catch (ClientException ce) { System.out.println("Caught an ClientException, which means the client encountered " + "a serious internal problem while trying to communicate with OSS, " + "such as not being able to access the network."); System.out.println("Error Message:" + ce.getMessage()); } finally { if (ossClient != null) { ossClient.shutdown(); } } } }
通過CopyObjectRequest拷貝
以下代碼用於通過CopyObjectRequest將srcexamplebucket中的srcexampleobject.txt檔案拷貝到目標儲存空間desexamplebucket中的desexampleobject.txt檔案。
import com.aliyun.oss.ClientException; import com.aliyun.oss.OSS; import com.aliyun.oss.common.auth.*; import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.OSSException; import com.aliyun.oss.model.*; public class Demo { public static void main(String[] args) throws Exception { // Endpoint以華東1(杭州)為例,其它Region請按實際情況填寫。 String endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider(); // 填寫源Bucket名稱。 String sourceBucketName = "srcexamplebucket"; // 填寫源Object的完整路徑。Object完整路徑中不能包含Bucket名稱。 String sourceKey = "srcexampleobject.txt"; // 填寫與源Bucket處於同一地區的目標Bucket名稱。 String destinationBucketName = "desexamplebucket"; // 填寫目標Object的完整路徑。Object完整路徑中不能包含Bucket名稱。 String destinationKey = "desexampleobject.txt"; // 填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為cn-hangzhou。 String region = "cn-hangzhou"; // 建立OSSClient執行個體。 ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration(); clientBuilderConfiguration.setSignatureVersion(SignVersion.V4); OSS ossClient = OSSClientBuilder.create() .endpoint(endpoint) .credentialsProvider(credentialsProvider) .clientConfiguration(clientBuilderConfiguration) .region(region) .build(); try { // 建立CopyObjectRequest對象。 CopyObjectRequest copyObjectRequest = new CopyObjectRequest(sourceBucketName, sourceKey, destinationBucketName, destinationKey); // 設定新的檔案中繼資料。 ObjectMetadata meta = new ObjectMetadata(); meta.setContentType("text/plain"); // 指定CopyObject操作時是否覆蓋同名目標Object。此處設定為true,表示禁止覆蓋同名Object。 // meta.setHeader("x-oss-forbid-overwrite", "true"); // 指定拷貝的源地址。 // meta.setHeader(OSSHeaders.COPY_OBJECT_SOURCE, "/examplebucket/recode-test.txt"); // 如果源Object的ETag值和您提供的ETag相等,則執行拷貝操作,並返回200 OK。 // meta.setHeader(OSSHeaders.COPY_OBJECT_SOURCE_IF_MATCH, "5B3C1A2E053D763E1B002CC607C5****"); // 如果源Object的ETag值和您提供的ETag不相等,則執行拷貝操作,並返回200 OK。 // meta.setHeader(OSSHeaders.COPY_OBJECT_SOURCE_IF_NONE_MATCH, "5B3C1A2E053D763E1B002CC607C5****"); // 如果指定的時間等於或者晚於檔案實際修改時間,則正常拷貝檔案,並返回200 OK。 // meta.setHeader(OSSHeaders.COPY_OBJECT_SOURCE_IF_UNMODIFIED_SINCE, "2021-12-09T07:01:56.000Z"); // 如果源Object在指定時間後被修改過,則執行拷貝操作。 // meta.setHeader(OSSHeaders.COPY_OBJECT_SOURCE_IF_MODIFIED_SINCE, "2021-12-09T07:01:56.000Z"); // 指定設定目標Object中繼資料的方式。此處設定為COPY,表示複製源Object的中繼資料到目標Object。 // meta.setHeader(OSSHeaders.COPY_OBJECT_METADATA_DIRECTIVE, "COPY"); // 指定OSS建立目標Object時使用的伺服器端密碼編譯演算法。 // meta.setHeader(OSSHeaders.OSS_SERVER_SIDE_ENCRYPTION, ObjectMetadata.KMS_SERVER_SIDE_ENCRYPTION); // 表示KMS託管的使用者主要金鑰,該參數僅在x-oss-server-side-encryption為KMS時有效。 // meta.setHeader(OSSHeaders.OSS_SERVER_SIDE_ENCRYPTION_KEY_ID, "9468da86-3509-4f8d-a61e-6eab1eac****"); // 指定OSS建立目標Object時的存取權限,此處設定為Private,表示只有Object的擁有者和授權使用者有該Object的讀寫權限,其他使用者沒有許可權操作該Object。 // meta.setHeader(OSSHeaders.OSS_OBJECT_ACL, CannedAccessControlList.Private); // 指定Object的儲存類型。此處設定為Standard,表示標準儲存類型。 // meta.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard); // 指定Object的對象標籤,可同時設定多個標籤。 // meta.setHeader(OSSHeaders.OSS_TAGGING, "a:1"); // 指定設定目標Object對象標籤的方式。此處設定為COPY,表示複製源Object的對象標籤到目標Object。 // meta.setHeader(OSSHeaders.COPY_OBJECT_TAGGING_DIRECTIVE, "COPY"); copyObjectRequest.setNewObjectMetadata(meta); // 複製檔案。 CopyObjectResult result = ossClient.copyObject(copyObjectRequest); System.out.println("ETag: " + result.getETag() + " LastModified: " + result.getLastModified()); } catch (OSSException oe) { System.out.println("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason."); System.out.println("Error Message:" + oe.getErrorMessage()); System.out.println("Error Code:" + oe.getErrorCode()); System.out.println("Request ID:" + oe.getRequestId()); System.out.println("Host ID:" + oe.getHostId()); } catch (ClientException ce) { System.out.println("Caught an ClientException, which means the client encountered " + "a serious internal problem while trying to communicate with OSS, " + "such as not being able to access the network."); System.out.println("Error Message:" + ce.getMessage()); } finally { if (ossClient != null) { ossClient.shutdown(); } } } }
拷貝大檔案
對於大於1 GB的檔案,需要使用分區拷貝(UploadPartCopy)。分區拷貝分為三步:
通過ossClient.initiateMultipartUpload初始化分區拷貝任務。
通過ossClient.uploadPartCopy進行分區拷貝。除最後一個分區外,其它分區都要大於100KB。
通過ossClient.completeMultipartUpload提交分區拷貝任務。
以下代碼用於通過分區拷貝將srcexamplebucket中的srcexampleobject.txt檔案拷貝到目標儲存空間desexamplebucket中的desexampleobject.txt檔案。
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.*;
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) throws Exception {
// Endpoint以華東1(杭州)為例,其它Region請按實際情況填寫。
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// 填寫源Bucket名稱。
String sourceBucketName = "srcexamplebucket";
// 填寫源Object的完整路徑。Object完整路徑中不能包含Bucket名稱。
String sourceKey = "srcexampleobject.txt";
// 填寫與源Bucket處於同一地區的目標Bucket名稱。
String destinationBucketName = "desexamplebucket";
// 填寫目標Object的完整路徑。Object完整路徑中不能包含Bucket名稱。
String destinationKey = "desexampleobject.txt";
// 填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為cn-hangzhou。
String region = "cn-hangzhou";
// 建立OSSClient執行個體。
ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
OSS ossClient = OSSClientBuilder.create()
.endpoint(endpoint)
.credentialsProvider(credentialsProvider)
.clientConfiguration(clientBuilderConfiguration)
.region(region)
.build();
try {
ObjectMetadata objectMetadata = ossClient.getObjectMetadata(sourceBucketName, sourceKey);
// 擷取被拷貝檔案的大小。
long contentLength = objectMetadata.getContentLength();
// 設定分區大小為10 MB。單位為位元組。
long partSize = 1024 * 1024 * 10;
// 計算分區總數。
int partCount = (int) (contentLength / partSize);
if (contentLength % partSize != 0) {
partCount++;
}
System.out.println("total part count:" + partCount);
// 初始化拷貝任務。可以通過InitiateMultipartUploadRequest指定目標檔案中繼資料。
InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(destinationBucketName, destinationKey);
// 拷貝源檔案ContentType和UserMetadata,分區拷貝預設不拷貝源檔案的ContentType和UserMetadata。
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(objectMetadata.getContentType());
metadata.setUserMetadata(objectMetadata.getUserMetadata());
initiateMultipartUploadRequest.setObjectMetadata(metadata);
InitiateMultipartUploadResult initiateMultipartUploadResult = ossClient.initiateMultipartUpload(initiateMultipartUploadRequest);
String uploadId = initiateMultipartUploadResult.getUploadId();
// 分區拷貝。
List<PartETag> partETags = new ArrayList<PartETag>();
for (int i = 0; i < partCount; i++) {
// 計算每個分區的大小。
long skipBytes = partSize * i;
long size = partSize < contentLength - skipBytes ? partSize : contentLength - skipBytes;
// 建立UploadPartCopyRequest。可以通過UploadPartCopyRequest指定限定條件。
UploadPartCopyRequest uploadPartCopyRequest =
new UploadPartCopyRequest(sourceBucketName, sourceKey, destinationBucketName, destinationKey);
uploadPartCopyRequest.setUploadId(uploadId);
uploadPartCopyRequest.setPartSize(size);
uploadPartCopyRequest.setBeginIndex(skipBytes);
uploadPartCopyRequest.setPartNumber(i + 1);
//Map headers = new HashMap();
// 指定拷貝的源地址。
// headers.put(OSSHeaders.COPY_OBJECT_SOURCE, "/examplebucket/desexampleobject.txt");
// 指定源Object的拷貝範圍。例如設定bytes=0~1023,表示拷貝1~1024位元組的內容。
// headers.put(OSSHeaders.COPY_SOURCE_RANGE, "bytes=0~1023");
// 如果源Object的ETag值和您提供的ETag相等,則執行拷貝操作,並返回200 OK。
// headers.put(OSSHeaders.COPY_OBJECT_SOURCE_IF_MATCH, "5B3C1A2E053D763E1B002CC607C5****");
// 如果源Object的ETag值和您提供的ETag不相等,則執行拷貝操作,並返回200 OK。
// headers.put(OSSHeaders.COPY_OBJECT_SOURCE_IF_NONE_MATCH, "5B3C1A2E053D763E1B002CC607C5****");
// 如果指定的時間等於或者晚於檔案實際修改時間,則正常拷貝檔案,並返回200 OK。
// headers.put(OSSHeaders.COPY_OBJECT_SOURCE_IF_UNMODIFIED_SINCE, "2021-12-09T07:01:56.000Z");
// 如果源Object在使用者指定的時間以後被修改過,則執行拷貝操作。
// headers.put(OSSHeaders.COPY_OBJECT_SOURCE_IF_MODIFIED_SINCE, "2021-12-09T07:01:56.000Z");
// uploadPartCopyRequest.setHeaders(headers);
UploadPartCopyResult uploadPartCopyResult = ossClient.uploadPartCopy(uploadPartCopyRequest);
// 將返回的分區ETag儲存到partETags中。
partETags.add(uploadPartCopyResult.getPartETag());
}
// 提交分區拷貝任務。
CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(
destinationBucketName, destinationKey, uploadId, partETags);
ossClient.completeMultipartUpload(completeMultipartUploadRequest);
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}
相關文檔
拷貝小檔案
關於拷貝小檔案的API介面說明,請參見CopyObject。
拷貝大檔案
關於拷貝大檔案的完整範例程式碼,請參見GitHub樣本。
關於拷貝大檔案的API介面說明,請參見UploadPartCopy。