Skip to content

Commit e783dc3

Browse files
nitishtminio-trusted
authored andcommitted
Implement CopyObject API
Fixes #463
1 parent 711b2aa commit e783dc3

File tree

5 files changed

+606
-40
lines changed

5 files changed

+606
-40
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Minio Java Library for Amazon S3 Compatible Cloud Storage,
3+
* (C) 2017 Minio,Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package io.minio;
19+
20+
import java.util.HashMap;
21+
import java.util.Map;
22+
import java.util.Collections;
23+
24+
import org.joda.time.DateTime;
25+
26+
import io.minio.errors.InvalidArgumentException;
27+
28+
/**
29+
* A container class to hold all the Conditions to be checked
30+
* before copying an object.
31+
*/
32+
public class CopyConditions {
33+
34+
private Map<String, String> copyConditions = new HashMap<>();
35+
36+
/**
37+
* Set modified condition, copy object modified since given time.
38+
*
39+
* @throws InvalidArgumentException
40+
* When date is null
41+
*/
42+
public void setModified(DateTime date) throws InvalidArgumentException {
43+
if (date == null) {
44+
throw new InvalidArgumentException("Date cannot be empty");
45+
}
46+
copyConditions.put("x-amz-copy-source-if-modified-since", date.toString(DateFormat.HTTP_HEADER_DATE_FORMAT));
47+
}
48+
49+
/**
50+
* Sets object unmodified condition, copy object unmodified since given time.
51+
*
52+
* @throws InvalidArgumentException
53+
* When date is null
54+
*/
55+
public void setUnmodified(DateTime date) throws InvalidArgumentException {
56+
if (date == null) {
57+
throw new InvalidArgumentException("Date can not be null");
58+
}
59+
60+
copyConditions.put("x-amz-copy-source-if-unmodified-since", date.toString(DateFormat.HTTP_HEADER_DATE_FORMAT));
61+
}
62+
63+
/**
64+
* Set matching ETag condition, copy object which matches
65+
* the following ETag.
66+
*
67+
* @throws InvalidArgumentException
68+
* When etag is null
69+
*/
70+
public void setMatchETag(String etag) throws InvalidArgumentException {
71+
if (etag == null) {
72+
throw new InvalidArgumentException("ETag cannot be empty");
73+
}
74+
copyConditions.put("x-amz-copy-source-if-match", etag);
75+
}
76+
77+
/**
78+
* Set matching ETag none condition, copy object which does not
79+
* match the following ETag.
80+
*
81+
* @throws InvalidArgumentException
82+
* When etag is null
83+
*/
84+
public void setMatchETagNone(String etag) throws InvalidArgumentException {
85+
if (etag == null) {
86+
throw new InvalidArgumentException("ETag cannot be empty");
87+
}
88+
copyConditions.put("x-amz-copy-source-if-none-match", etag);
89+
}
90+
91+
/**
92+
* Get all the set copy conditions map.
93+
*
94+
*/
95+
public Map<String, String> getConditions() {
96+
return Collections.unmodifiableMap(copyConditions);
97+
}
98+
}

api/src/main/java/io/minio/MinioClient.java

Lines changed: 188 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
2-
* Minio Java Library for Amazon S3 Compatible Cloud Storage, (C) 2015 Minio, Inc.
2+
* Minio Java Library for Amazon S3 Compatible Cloud Storage,
3+
* (C) 2015, 2016, 2017 Minio, Inc.
34
*
45
* Licensed under the Apache License, Version 2.0 (the "License");
56
* you may not use this file except in compliance with the License.
@@ -38,6 +39,7 @@
3839
import io.minio.http.Scheme;
3940
import io.minio.messages.Bucket;
4041
import io.minio.messages.CompleteMultipartUpload;
42+
import io.minio.messages.CopyObjectResult;
4143
import io.minio.messages.CreateBucketConfiguration;
4244
import io.minio.messages.ErrorResponse;
4345
import io.minio.messages.InitiateMultipartUploadResult;
@@ -1076,7 +1078,6 @@ private HttpResponse executePut(String bucketName, String objectName, Map<String
10761078
HttpResponse response = execute(Method.PUT, region, bucketName, objectName,
10771079
headerMap, queryParamMap,
10781080
data, length);
1079-
response.body().close();
10801081
return response;
10811082
}
10821083

@@ -1397,6 +1398,186 @@ public void getObject(String bucketName, String objectName, String fileName)
13971398
}
13981399
}
13991400

1401+
/**
1402+
* Copy a source object into a new destination object with same object name.
1403+
*
1404+
* </p>
1405+
* <b>Example:</b><br>
1406+
*
1407+
* <pre>
1408+
* {@code minioClient.copyObject("my-bucketname", "my-objectname", "my-destbucketname");}
1409+
* </pre>
1410+
*
1411+
* @param bucketName
1412+
* Bucket name where the object to be copied exists.
1413+
* @param objectName
1414+
* Object name source to be copied.
1415+
* @param destBucketName
1416+
* Bucket name where the object will be copied to.
1417+
*
1418+
* @throws InvalidBucketNameException
1419+
* upon an invalid bucket name
1420+
* @throws NoSuchAlgorithmException
1421+
* upon requested algorithm was not found during signature calculation
1422+
* @throws InvalidKeyException
1423+
* upon an invalid access key or secret key
1424+
*/
1425+
public void copyObject(String bucketName, String objectName, String destBucketName)
1426+
throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException,
1427+
NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
1428+
InvalidArgumentException {
1429+
1430+
copyObject(bucketName, objectName, destBucketName, null, null);
1431+
}
1432+
1433+
/**
1434+
* Copy a source object into a new destination object.
1435+
*
1436+
* </p>
1437+
* <b>Example:</b><br>
1438+
*
1439+
* <pre>
1440+
* {@code minioClient.copyObject("my-bucketname", "my-objectname", "my-destbucketname", "my-destobjname");}
1441+
* </pre>
1442+
*
1443+
* @param bucketName
1444+
* Bucket name where the object to be copied exists.
1445+
* @param objectName
1446+
* Object name source to be copied.
1447+
* @param destBucketName
1448+
* Bucket name where the object will be copied to.
1449+
* @param destObjectName
1450+
* Object name to be created, if not provided uses source object name
1451+
* as destination object name.
1452+
*
1453+
* @throws InvalidBucketNameException
1454+
* upon an invalid bucket name
1455+
* @throws NoSuchAlgorithmException
1456+
* upon requested algorithm was not found during signature calculation
1457+
* @throws InvalidKeyException
1458+
* upon an invalid access key or secret key
1459+
*/
1460+
public void copyObject(String bucketName, String objectName, String destBucketName, String destObjectName)
1461+
throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException,
1462+
NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
1463+
InvalidArgumentException {
1464+
1465+
copyObject(bucketName, objectName, destBucketName, destObjectName, null);
1466+
}
1467+
1468+
/**
1469+
* Copy a source object into a new object with the provided name in the provided bucket.
1470+
* optionally can take a key value CopyConditions as well for conditionally attempting
1471+
* copyObject.
1472+
*
1473+
* </p>
1474+
* <b>Example:</b><br>
1475+
*
1476+
* <pre>
1477+
* {@code minioClient.copyObject("my-bucketname", "my-objectname", "my-destbucketname",
1478+
* copyConditions);}
1479+
* </pre>
1480+
*
1481+
* @param bucketName
1482+
* Bucket name where the object to be copied exists.
1483+
* @param objectName
1484+
* Object name source to be copied.
1485+
* @param destBucketName
1486+
* Bucket name where the object will be copied to.
1487+
* @param copyConditions
1488+
* CopyConditions object with collection of supported CopyObject conditions.
1489+
*
1490+
* @throws InvalidBucketNameException
1491+
* upon an invalid bucket name
1492+
* @throws NoSuchAlgorithmException
1493+
* upon requested algorithm was not found during signature calculation
1494+
* @throws InvalidKeyException
1495+
* upon an invalid access key or secret key
1496+
*/
1497+
public void copyObject(String bucketName, String objectName, String destBucketName,
1498+
CopyConditions copyConditions)
1499+
throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException,
1500+
NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
1501+
InvalidArgumentException {
1502+
1503+
copyObject(bucketName, objectName, destBucketName, null, copyConditions);
1504+
}
1505+
1506+
/**
1507+
* Copy a source object into a new object with the provided name in the provided bucket.
1508+
* optionally can take a key value CopyConditions as well for conditionally attempting
1509+
* copyObject.
1510+
*
1511+
* </p>
1512+
* <b>Example:</b><br>
1513+
*
1514+
* <pre>
1515+
* {@code minioClient.copyObject("my-bucketname", "my-objectname", "my-destbucketname",
1516+
* "my-destobjname", copyConditions);}
1517+
* </pre>
1518+
*
1519+
* @param bucketName
1520+
* Bucket name where the object to be copied exists.
1521+
* @param objectName
1522+
* Object name source to be copied.
1523+
* @param destBucketName
1524+
* Bucket name where the object will be copied to.
1525+
* @param destObjectName
1526+
* Object name to be created, if not provided uses source object name
1527+
* as destination object name.
1528+
* @param copyConditions
1529+
* CopyConditions object with collection of supported CopyObject conditions.
1530+
*
1531+
* @throws InvalidBucketNameException
1532+
* upon an invalid bucket name, invalid object name.
1533+
* @throws NoSuchAlgorithmException
1534+
* upon requested algorithm was not found during signature calculation
1535+
* @throws InvalidKeyException
1536+
* upon an invalid access key or secret key
1537+
*/
1538+
public void copyObject(String bucketName, String objectName, String destBucketName,
1539+
String destObjectName, CopyConditions copyConditions)
1540+
throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException,
1541+
NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
1542+
InvalidArgumentException {
1543+
1544+
if (bucketName == null) {
1545+
throw new InvalidArgumentException("Source bucket name cannot be empty");
1546+
}
1547+
if (objectName == null) {
1548+
throw new InvalidArgumentException("Source object name cannot be empty");
1549+
}
1550+
if (destBucketName == null) {
1551+
throw new InvalidArgumentException("Destination bucket name cannot be empty");
1552+
}
1553+
1554+
// Escape source object path.
1555+
String sourceObjectPath = S3Escaper.encode(Paths.get(bucketName, objectName).toString());
1556+
1557+
// Destination object name is optional, if empty default to source object name.
1558+
if (destObjectName == null) {
1559+
destObjectName = objectName;
1560+
}
1561+
1562+
Map<String, String> headerMap = new HashMap<>();
1563+
1564+
// Set the object source
1565+
headerMap.put("x-amz-copy-source", sourceObjectPath);
1566+
1567+
// If no conditions available, skip addition else add the conditions to the header
1568+
if (copyConditions != null) {
1569+
headerMap.putAll(copyConditions.getConditions());
1570+
}
1571+
1572+
1573+
HttpResponse response = executePut(destBucketName, destObjectName, headerMap,
1574+
null, null, "", 0);
1575+
1576+
// For now ignore the copyObjectResult, just read and parse it.
1577+
CopyObjectResult result = new CopyObjectResult();
1578+
result.parseXml(response.body().charStream());
1579+
response.body().close();
1580+
}
14001581

14011582
/**
14021583
* Returns an presigned URL to download the object in the bucket with given expiry time with custom request params.
@@ -1946,7 +2127,8 @@ public void makeBucket(String bucketName, String region)
19462127
configString = config.toString();
19472128
}
19482129

1949-
executePut(bucketName, null, null, null, US_EAST_1, configString, 0);
2130+
HttpResponse response = executePut(bucketName, null, null, null, US_EAST_1, configString, 0);
2131+
response.body().close();
19502132
}
19512133

19522134

@@ -2164,6 +2346,7 @@ private String putObject(String bucketName, String objectName, int length,
21642346
}
21652347

21662348
HttpResponse response = executePut(bucketName, objectName, headerMap, queryParamMap, data, length);
2349+
response.body().close();
21672350
return response.header().etag();
21682351
}
21692352

@@ -2307,7 +2490,8 @@ private void setBucketPolicy(String bucketName, BucketPolicy policy)
23072490

23082491
String policyJson = policy.getJson();
23092492

2310-
executePut(bucketName, null, headerMap, queryParamMap, policyJson, policyJson.length());
2493+
HttpResponse response = executePut(bucketName, null, headerMap, queryParamMap, policyJson, policyJson.length());
2494+
response.body().close();
23112495
}
23122496

23132497

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Minio Java Library for Amazon S3 Compatible Cloud Storage,
3+
* (C) 2017 Minio, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package io.minio.messages;
19+
20+
import org.joda.time.DateTime;
21+
import com.google.api.client.util.Key;
22+
import org.xmlpull.v1.XmlPullParserException;
23+
import io.minio.DateFormat;
24+
25+
/**
26+
* Helper class to parse Amazon AWS S3 response XML containing
27+
* object item information.
28+
*/
29+
@SuppressWarnings({ "SameParameterValue", "unused" })
30+
public class CopyObjectResult extends XmlEntity {
31+
@Key("ETag")
32+
private String etag;
33+
@Key("LastModified")
34+
private String lastModified;
35+
36+
public CopyObjectResult() throws XmlPullParserException {
37+
super();
38+
super.name = "CopyObjectResult";
39+
}
40+
41+
/**
42+
* Returns last modified time of the object.
43+
*/
44+
public DateTime lastModified() {
45+
return DateFormat.RESPONSE_DATE_FORMAT.parseDateTime(lastModified).toDateTime();
46+
}
47+
48+
/**
49+
* Returns ETag of the object.
50+
*/
51+
public String etag() {
52+
return etag;
53+
}
54+
}

0 commit comments

Comments
 (0)