From 82b3a20054e47ad3fe271f13aad653a23f2e202b Mon Sep 17 00:00:00 2001 From: wujinhu Date: Mon, 21 Dec 2020 19:35:45 +0800 Subject: [PATCH] support copyObjects API --- src/main/java/com/aliyun/oss/OSS.java | 13 +++ src/main/java/com/aliyun/oss/OSSClient.java | 5 ++ .../oss/common/parser/RequestMarshallers.java | 22 +++++ .../oss/internal/OSSObjectOperation.java | 18 ++++ .../oss/internal/RequestParameters.java | 1 + .../aliyun/oss/internal/ResponseParsers.java | 54 +++++++++++- .../aliyun/oss/internal/SignParameters.java | 2 +- .../aliyun/oss/model/CopyObjectEntity.java | 49 +++++++++++ .../aliyun/oss/model/CopyObjectsRequest.java | 53 +++++++++++ .../aliyun/oss/model/CopyObjectsResult.java | 67 ++++++++++++++ .../oss/model/SingleCopyObjectResult.java | 88 +++++++++++++++++++ 11 files changed, 370 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/aliyun/oss/model/CopyObjectEntity.java create mode 100644 src/main/java/com/aliyun/oss/model/CopyObjectsRequest.java create mode 100644 src/main/java/com/aliyun/oss/model/CopyObjectsResult.java create mode 100644 src/main/java/com/aliyun/oss/model/SingleCopyObjectResult.java diff --git a/src/main/java/com/aliyun/oss/OSS.java b/src/main/java/com/aliyun/oss/OSS.java index 8fbfcce2..8d2b168d 100644 --- a/src/main/java/com/aliyun/oss/OSS.java +++ b/src/main/java/com/aliyun/oss/OSS.java @@ -1098,6 +1098,19 @@ public CopyObjectResult copyObject(String sourceBucketName, String sourceKey, St */ public CopyObjectResult copyObject(CopyObjectRequest copyObjectRequest) throws OSSException, ClientException; + /** + * Copies existing keys to target keys in an OSS bucket. + * If target key exist, it would be overwritten by the source key. + * + * @param copyObjectsRequest + * A {@link CopyObjectsRequest} instance that specifies bucket, + * source keys and target keys. + * @return A {@link CopyObjectsResult} instance. + * @throws OSSException + * @throws ClientException + */ + public CopyObjectsResult copyObjects(CopyObjectsRequest copyObjectsRequest) throws OSSException, ClientException; + /** * Gets a {@link OSSObject} from {@link Bucket}. * diff --git a/src/main/java/com/aliyun/oss/OSSClient.java b/src/main/java/com/aliyun/oss/OSSClient.java index 54b99f2c..bd93e017 100644 --- a/src/main/java/com/aliyun/oss/OSSClient.java +++ b/src/main/java/com/aliyun/oss/OSSClient.java @@ -592,6 +592,11 @@ public CopyObjectResult copyObject(CopyObjectRequest copyObjectRequest) throws O return objectOperation.copyObject(copyObjectRequest); } + @Override + public CopyObjectsResult copyObjects(CopyObjectsRequest copyObjectsRequest) throws OSSException, ClientException { + return objectOperation.copyObjects(copyObjectsRequest); + } + @Override public OSSObject getObject(String bucketName, String key) throws OSSException, ClientException { return this.getObject(new GetObjectRequest(bucketName, key)); diff --git a/src/main/java/com/aliyun/oss/common/parser/RequestMarshallers.java b/src/main/java/com/aliyun/oss/common/parser/RequestMarshallers.java index 7a2f34f3..5a6a1f0e 100644 --- a/src/main/java/com/aliyun/oss/common/parser/RequestMarshallers.java +++ b/src/main/java/com/aliyun/oss/common/parser/RequestMarshallers.java @@ -94,6 +94,7 @@ public final class RequestMarshallers { public static final DeleteVpcipRequestMarshaller deleteVpcipRequestMarshaller = new DeleteVpcipRequestMarshaller(); public static final DeleteBucketVpcipRequestMarshaller deleteBucketVpcipRequestMarshaller = new DeleteBucketVpcipRequestMarshaller(); + public static final CopyObjectsRequestMarshaller copyObjectsRequestMarshaller = new CopyObjectsRequestMarshaller(); public interface RequestMarshaller extends Marshaller { } @@ -102,6 +103,27 @@ public interface RequestMarshaller2 extends Marshaller { } + public static final class CopyObjectsRequestMarshaller implements RequestMarshaller { + + @Override + public FixedLengthInputStream marshall(CopyObjectsRequest request) { + StringBuffer xmlBody = new StringBuffer(); + xmlBody.append(""); + for (CopyObjectEntity entity : request.getCopyObjectEntities()) { + xmlBody.append(""); + xmlBody.append(""); + xmlBody.append(entity.getSourceKey()); + xmlBody.append(""); + xmlBody.append(""); + xmlBody.append(entity.getTargetKey()); + xmlBody.append(""); + xmlBody.append(""); + } + xmlBody.append(""); + return stringMarshaller.marshall(xmlBody.toString()); + } + } + public static final class StringMarshaller implements Marshaller { @Override diff --git a/src/main/java/com/aliyun/oss/internal/OSSObjectOperation.java b/src/main/java/com/aliyun/oss/internal/OSSObjectOperation.java index e09ec1e5..63789bbc 100644 --- a/src/main/java/com/aliyun/oss/internal/OSSObjectOperation.java +++ b/src/main/java/com/aliyun/oss/internal/OSSObjectOperation.java @@ -48,12 +48,14 @@ import static com.aliyun.oss.internal.OSSUtils.safeCloseResponse; import static com.aliyun.oss.internal.RequestParameters.ENCODING_TYPE; import static com.aliyun.oss.internal.RequestParameters.SUBRESOURCE_ACL; +import static com.aliyun.oss.internal.RequestParameters.SUBRESOURCE_COPY; import static com.aliyun.oss.internal.RequestParameters.SUBRESOURCE_DELETE; import static com.aliyun.oss.internal.RequestParameters.SUBRESOURCE_OBJECTMETA; import static com.aliyun.oss.internal.RequestParameters.SUBRESOURCE_SYMLINK; import static com.aliyun.oss.internal.RequestParameters.SUBRESOURCE_TAGGING; import static com.aliyun.oss.internal.ResponseParsers.appendObjectResponseParser; import static com.aliyun.oss.internal.ResponseParsers.copyObjectResponseParser; +import static com.aliyun.oss.internal.ResponseParsers.copyObjectsResponseParser; import static com.aliyun.oss.internal.ResponseParsers.deleteObjectsResponseParser; import static com.aliyun.oss.internal.ResponseParsers.getTaggingResponseParser; import static com.aliyun.oss.internal.ResponseParsers.getObjectAclResponseParser; @@ -496,6 +498,22 @@ public CopyObjectResult copyObject(CopyObjectRequest copyObjectRequest) throws O copyObjectRequest.getDestinationKey(), true); } + /** + * Copy existing objects to targets. + */ + public CopyObjectsResult copyObjects(CopyObjectsRequest copyObjectsRequest) throws OSSException, ClientException { + assertParameterNotNull(copyObjectsRequest, "copyObjectsRequest"); + + Map params = new HashMap(); + params.put(SUBRESOURCE_COPY, null); + + RequestMessage request = new OSSRequestMessageBuilder(getInnerClient()).setEndpoint(getEndpoint()) + .setMethod(HttpMethod.POST).setBucket(copyObjectsRequest.getBucketName()) + .setInputStreamWithLength(copyObjectsRequestMarshaller.marshall(copyObjectsRequest)) + .setParameters(params).setOriginalRequest(copyObjectsRequest).build(); + return doOperation(request, copyObjectsResponseParser, copyObjectsRequest.getBucketName(), null, true); + } + /** * Delete an object. */ diff --git a/src/main/java/com/aliyun/oss/internal/RequestParameters.java b/src/main/java/com/aliyun/oss/internal/RequestParameters.java index cde59c3c..f94adb97 100644 --- a/src/main/java/com/aliyun/oss/internal/RequestParameters.java +++ b/src/main/java/com/aliyun/oss/internal/RequestParameters.java @@ -30,6 +30,7 @@ public final class RequestParameters { public static final String SUBRESOURCE_UPLOADS = "uploads"; public static final String SUBRESOURCE_DELETE = "delete"; public static final String SUBRESOURCE_CORS = "cors"; + public static final String SUBRESOURCE_COPY = "copy"; public static final String SUBRESOURCE_APPEND = "append"; public static final String SUBRESOURCE_TAGGING = "tagging"; public static final String SUBRESOURCE_IMG = "img"; diff --git a/src/main/java/com/aliyun/oss/internal/ResponseParsers.java b/src/main/java/com/aliyun/oss/internal/ResponseParsers.java index aa26ff3a..f1699acb 100644 --- a/src/main/java/com/aliyun/oss/internal/ResponseParsers.java +++ b/src/main/java/com/aliyun/oss/internal/ResponseParsers.java @@ -80,6 +80,7 @@ import com.aliyun.oss.model.CnameConfiguration; import com.aliyun.oss.model.CompleteMultipartUploadResult; import com.aliyun.oss.model.CopyObjectResult; +import com.aliyun.oss.model.CopyObjectsResult; import com.aliyun.oss.model.CreateLiveChannelResult; import com.aliyun.oss.model.DataRedundancyType; import com.aliyun.oss.model.DeleteObjectsResult; @@ -126,6 +127,7 @@ import com.aliyun.oss.model.Permission; import com.aliyun.oss.model.PutObjectResult; import com.aliyun.oss.model.PushflowStatus; +import com.aliyun.oss.model.SingleCopyObjectResult; import com.aliyun.oss.model.SetBucketCORSRequest.CORSRule; import com.aliyun.oss.model.CORSConfiguration; import com.aliyun.oss.model.SimplifiedObjectMeta; @@ -196,6 +198,7 @@ public final class ResponseParsers { public static final AppendObjectResponseParser appendObjectResponseParser = new AppendObjectResponseParser(); public static final GetObjectMetadataResponseParser getObjectMetadataResponseParser = new GetObjectMetadataResponseParser(); public static final CopyObjectResponseParser copyObjectResponseParser = new CopyObjectResponseParser(); + public static final CopyObjectsResponseParser copyObjectsResponseParser = new CopyObjectsResponseParser(); public static final DeleteObjectsResponseParser deleteObjectsResponseParser = new DeleteObjectsResponseParser(); public static final DeleteVersionsResponseParser deleteVersionsResponseParser = new DeleteVersionsResponseParser(); public static final GetObjectAclResponseParser getObjectAclResponseParser = new GetObjectAclResponseParser(); @@ -995,7 +998,22 @@ public DeleteObjectsResult parse(ResponseMessage response) throws ResponseParseE } } - + + public static final class CopyObjectsResponseParser implements ResponseParser { + + @Override + public CopyObjectsResult parse(ResponseMessage response) throws ResponseParseException { + try { + CopyObjectsResult result = parseCopyObjectsResult(response.getContent()); + result.setRequestId(response.getRequestId()); + result.setResponse(response); + return result; + } finally { + safeCloseResponse(response); + } + } + } + public static final class DeleteVersionsResponseParser implements ResponseParser { @Override @@ -2201,6 +2219,40 @@ public static CopyObjectResult parseCopyObjectResult(InputStream responseBody) t } } + /** + * Unmarshall copy objects response body to corresponding result. + */ + public static CopyObjectsResult parseCopyObjectsResult(InputStream responseBody) throws ResponseParseException { + try { + Element root = getXmlRootElement(responseBody); + CopyObjectsResult result = new CopyObjectsResult(); + for (Element element : root.getChildren()) { + if (element.getName().equals("Success")) { + for (Element object : element.getChildren()) { + result.addSuccessResult( + SingleCopyObjectResult.success( + object.getChild("SourceKey").getText(), + object.getChild("TargetKey").getText(), + object.getChild("ETag").getText())); + } + } else if (element.getName().equals("Failed")) { + for (Element object : element.getChildren()) { + result.addFailureResult( + SingleCopyObjectResult.failure( + object.getChild("SourceKey").getText(), + object.getChild("TargetKey").getText(), + object.getChild("ErrorStatus").getText())); + } + } else { + throw new Exception("Invalid xml node: " + element.getName()); + } + } + return result; + } catch (Exception e) { + throw new ResponseParseException(e.getMessage(), e); + } + } + /** * Unmarshall delete objects response body to corresponding result. */ diff --git a/src/main/java/com/aliyun/oss/internal/SignParameters.java b/src/main/java/com/aliyun/oss/internal/SignParameters.java index 8701a083..193e1361 100644 --- a/src/main/java/com/aliyun/oss/internal/SignParameters.java +++ b/src/main/java/com/aliyun/oss/internal/SignParameters.java @@ -40,7 +40,7 @@ public class SignParameters { SUBRESOURCE_ENCRYPTION, SUBRESOURCE_POLICY, SUBRESOURCE_REQUEST_PAYMENT, OSS_TRAFFIC_LIMIT, SUBRESOURCE_QOS_INFO, SUBRESOURCE_ASYNC_FETCH, SEQUENTIAL, OSS_REQUEST_PAYER, VPCIP, VIP, SUBRESOURCE_INVENTORY, SUBRESOURCE_INVENTORY_ID, SUBRESOURCE_CONTINUATION_TOKEN, SUBRESOURCE_WORM, - SUBRESOURCE_WORM_ID, SUBRESOURCE_WORM_EXTEND, SUBRESOURCE_CALLBACK, SUBRESOURCE_CALLBACK_VAR}); + SUBRESOURCE_WORM_ID, SUBRESOURCE_WORM_EXTEND, SUBRESOURCE_CALLBACK, SUBRESOURCE_CALLBACK_VAR,SUBRESOURCE_COPY}); } diff --git a/src/main/java/com/aliyun/oss/model/CopyObjectEntity.java b/src/main/java/com/aliyun/oss/model/CopyObjectEntity.java new file mode 100644 index 00000000..16e7f8ee --- /dev/null +++ b/src/main/java/com/aliyun/oss/model/CopyObjectEntity.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.aliyun.oss.model; + +/** + * Copy object entity used by {@link CopyObjectsRequest}. + */ +public class CopyObjectEntity { + private String sourceKey; + private String targetKey; + + public CopyObjectEntity(String sourceKey, String targetKey) { + setSourceKey(sourceKey); + setTargetKey(targetKey); + } + + public String getSourceKey() { + return sourceKey; + } + + public void setSourceKey(String sourceKey) { + this.sourceKey = sourceKey; + } + + public String getTargetKey() { + return targetKey; + } + + public void setTargetKey(String targetKey) { + this.targetKey = targetKey; + } +} diff --git a/src/main/java/com/aliyun/oss/model/CopyObjectsRequest.java b/src/main/java/com/aliyun/oss/model/CopyObjectsRequest.java new file mode 100644 index 00000000..fb5365a5 --- /dev/null +++ b/src/main/java/com/aliyun/oss/model/CopyObjectsRequest.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.aliyun.oss.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * The request class that is used to copy objects. It wraps all parameters + * needed to copy objects. + */ +public class CopyObjectsRequest extends WebServiceRequest { + private String bucketName; + private List copyObjectEntities; + + public CopyObjectsRequest(String bucketName) { + setBucketName(bucketName); + this.copyObjectEntities = new ArrayList(); + } + + public String getBucketName() { + return bucketName; + } + + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + public void addCopyObjectEntity(CopyObjectEntity copyObjectEntity) { + copyObjectEntities.add(copyObjectEntity); + } + + public List getCopyObjectEntities() { + return copyObjectEntities; + } +} diff --git a/src/main/java/com/aliyun/oss/model/CopyObjectsResult.java b/src/main/java/com/aliyun/oss/model/CopyObjectsResult.java new file mode 100644 index 00000000..a4a65d24 --- /dev/null +++ b/src/main/java/com/aliyun/oss/model/CopyObjectsResult.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.aliyun.oss.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * The result of copying existing OSS objects. + */ +public class CopyObjectsResult extends GenericResult { + private List successResults; + private List failureResults; + + public CopyObjectsResult() { + successResults = new ArrayList(); + failureResults = new ArrayList(); + } + + public void addSuccessResult(SingleCopyObjectResult successResult) { + successResults.add(successResult); + } + + public void addFailureResult(SingleCopyObjectResult failureResult) { + failureResults.add(failureResult); + } + + public List getSuccessResults() { + return successResults; + } + + public List getFailureResults() { + return failureResults; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("CopyObjectsResult:\n"); + sb.append("\tSuccessObjects:\n"); + for (SingleCopyObjectResult result : successResults) { + sb.append("\t\t").append(result.toString()).append("\n"); + } + sb.append("\tFailureObjects:\n"); + for (SingleCopyObjectResult result : failureResults) { + sb.append("\t\t").append(result.toString()).append("\n"); + } + return sb.toString(); + } +} diff --git a/src/main/java/com/aliyun/oss/model/SingleCopyObjectResult.java b/src/main/java/com/aliyun/oss/model/SingleCopyObjectResult.java new file mode 100644 index 00000000..11102c2a --- /dev/null +++ b/src/main/java/com/aliyun/oss/model/SingleCopyObjectResult.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.aliyun.oss.model; + +/** + * Single copy object result. + * @see {@link CopyObjectsResult}. + */ +public class SingleCopyObjectResult { + private String sourceKey; + private String targetKey; + private String etag; + private String errorStatus; + + public static SingleCopyObjectResult success(String sourceKey, String targetKey, String etag) { + SingleCopyObjectResult result = new SingleCopyObjectResult(); + result.setSourceKey(sourceKey); + result.setTargetKey(targetKey); + result.setEtag(etag); + return result; + } + + public static SingleCopyObjectResult failure(String sourceKey, String targetKey, String errorStatus) { + SingleCopyObjectResult result = new SingleCopyObjectResult(); + result.setSourceKey(sourceKey); + result.setTargetKey(targetKey); + result.setErrorStatus(errorStatus); + return result; + } + + public String getSourceKey() { + return sourceKey; + } + + public void setSourceKey(String sourceKey) { + this.sourceKey = sourceKey; + } + + public String getTargetKey() { + return targetKey; + } + + public void setTargetKey(String targetKey) { + this.targetKey = targetKey; + } + + public String getEtag() { + return etag; + } + + public void setEtag(String etag) { + this.etag = etag; + } + + public String getErrorStatus() { + return errorStatus; + } + + public void setErrorStatus(String errorStatus) { + this.errorStatus = errorStatus; + } + + @Override + public String toString() { + if (etag != null) { + return "SourceKey: " + sourceKey + ", TargetKey: " + targetKey + ", ETag: " + etag; + } else { + return "SourceKey: " + sourceKey + ", TargetKey: " + targetKey + ", ErrorStatus: " + errorStatus; + } + } +}