Skip to content

Commit 1f899d0

Browse files
harshavardhanadeekoder
authored andcommitted
api: Enhance copyObject to support metadata. (#583)
1 parent 5e0e7d2 commit 1f899d0

File tree

5 files changed

+197
-8
lines changed

5 files changed

+197
-8
lines changed

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
* before copying an object.
3131
*/
3232
public class CopyConditions {
33+
// Metadata directive "REPLACE" used to replace metadata on
34+
// destination object in copyObject().
35+
private static final String METADATA_DIRECTIVE_REPLACE = "REPLACE";
3336

3437
private Map<String, String> copyConditions = new HashMap<>();
3538

@@ -88,6 +91,15 @@ public void setMatchETagNone(String etag) throws InvalidArgumentException {
8891
copyConditions.put("x-amz-copy-source-if-none-match", etag);
8992
}
9093

94+
/**
95+
* Set replace metadata directive which specifies that
96+
* destination object after copyObject() sets new
97+
* metadata provided in the request.
98+
*/
99+
public void setReplaceMetadataDirective() {
100+
copyConditions.put("x-amz-metadata-directive", METADATA_DIRECTIVE_REPLACE);
101+
}
102+
91103
/**
92104
* Get all the set copy conditions map.
93105
*

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

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,7 +1807,7 @@ public void copyObject(String bucketName, String objectName, String destBucketNa
18071807
NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
18081808
InvalidArgumentException {
18091809

1810-
copyObject(bucketName, objectName, destBucketName, null, null);
1810+
copyObject(bucketName, objectName, destBucketName, null, null, null);
18111811
}
18121812

18131813
/**
@@ -1842,7 +1842,7 @@ public void copyObject(String bucketName, String objectName, String destBucketNa
18421842
NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
18431843
InvalidArgumentException {
18441844

1845-
copyObject(bucketName, objectName, destBucketName, destObjectName, null);
1845+
copyObject(bucketName, objectName, destBucketName, destObjectName, null, null);
18461846
}
18471847

18481848
/**
@@ -1880,7 +1880,7 @@ public void copyObject(String bucketName, String objectName, String destBucketNa
18801880
NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
18811881
InvalidArgumentException {
18821882

1883-
copyObject(bucketName, objectName, destBucketName, null, copyConditions);
1883+
copyObject(bucketName, objectName, destBucketName, null, copyConditions, null);
18841884
}
18851885

18861886
/**
@@ -1921,6 +1921,51 @@ public void copyObject(String bucketName, String objectName, String destBucketNa
19211921
NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
19221922
InvalidArgumentException {
19231923

1924+
copyObject(bucketName, objectName, destBucketName, destObjectName, copyConditions, null);
1925+
}
1926+
1927+
/**
1928+
* Copy a source object into a new object with the provided name in the provided bucket.
1929+
* optionally can take a key value CopyConditions as well for conditionally attempting
1930+
* copyObject.
1931+
*
1932+
* </p>
1933+
* <b>Example:</b><br>
1934+
*
1935+
* <pre>
1936+
* {@code minioClient.copyObject("my-bucketname", "my-objectname", "my-destbucketname",
1937+
* "my-destobjname", copyConditions, metadata);}
1938+
* </pre>
1939+
*
1940+
* @param bucketName
1941+
* Bucket name where the object to be copied exists.
1942+
* @param objectName
1943+
* Object name source to be copied.
1944+
* @param destBucketName
1945+
* Bucket name where the object will be copied to.
1946+
* @param destObjectName
1947+
* Object name to be created, if not provided uses source object name
1948+
* as destination object name.
1949+
* @param copyConditions
1950+
* CopyConditions object with collection of supported CopyObject conditions.
1951+
* @param metadata
1952+
* Additional metadata to set on the destination object when
1953+
* setMetadataDirective is set to 'REPLACE'.
1954+
*
1955+
* @throws InvalidBucketNameException
1956+
* upon an invalid bucket name, invalid object name.
1957+
* @throws NoSuchAlgorithmException
1958+
* upon requested algorithm was not found during signature calculation
1959+
* @throws InvalidKeyException
1960+
* upon an invalid access key or secret key
1961+
*/
1962+
public void copyObject(String bucketName, String objectName, String destBucketName,
1963+
String destObjectName, CopyConditions copyConditions,
1964+
Map<String,String> metadata)
1965+
throws InvalidKeyException, InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException,
1966+
NoResponseException, ErrorResponseException, InternalException, IOException, XmlPullParserException,
1967+
InvalidArgumentException {
1968+
19241969
if (bucketName == null) {
19251970
throw new InvalidArgumentException("Source bucket name cannot be empty");
19261971
}
@@ -1949,6 +1994,11 @@ public void copyObject(String bucketName, String objectName, String destBucketNa
19491994
headerMap.putAll(copyConditions.getConditions());
19501995
}
19511996

1997+
// Set metadata on the destination of object.
1998+
if (metadata != null) {
1999+
headerMap.putAll(metadata);
2000+
}
2001+
19522002
HttpResponse response = executePut(destBucketName, destObjectName, headerMap,
19532003
null, "", 0);
19542004

docs/API.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,24 +1162,24 @@ try {
11621162
```
11631163

11641164
<a name="copyObject"></a>
1165-
### copyObject(String bucketName, String objectName, String destBucketName, String destObjectName, CopyConditions cpConds)
1165+
### copyObject(String bucketName, String objectName, String destBucketName, String destObjectName, CopyConditions cpConds, Map<String, String> metadata)
11661166

1167-
*`public void copyObject(String bucketName, String objectName, String destBucketName, String destObjectName, CopyConditions cpConds)`*
1167+
*`public void copyObject(String bucketName, String objectName, String destBucketName, String destObjectName, CopyConditions cpConds, Map<String, String> metadata)`*
11681168

11691169
Copies content from objectName to destObjectName.
11701170

11711171
[View Javadoc](http://minio.github.io/minio-java/io/minio/MinioClient.html#copyObject-java.lang.String-java.lang.String-java.lang.String-java.lang.String-io.minio.CopyConditions-)
11721172

11731173
__Parameters__
11741174

1175-
11761175
|Param | Type | Description |
11771176
|:--- |:--- |:--- |
11781177
| ``bucketName`` | _String_ | Name of the source bucket. |
11791178
| ``objectName`` | _String_ | Object name in the source bucket to be copied. |
11801179
| ``destBucketName`` | _String_ | Destination bucket name. |
11811180
| ``destObjectName`` | _String_ | Destination object name to be created, if not provided defaults to source object name.|
11821181
| ``copyConditions`` | _CopyConditions_ | Map of conditions useful for applying restrictions on copy operation.|
1182+
| ``metadata`` | _Map_ | Map of object metadata for destination object.|
11831183

11841184

11851185
| Return Type | Exceptions |

examples/CopyObjectMetadata.java

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Minio Java SDK 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+
* https://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+
import java.lang.StringBuilder;
19+
import java.io.ByteArrayInputStream;
20+
import java.io.IOException;
21+
import java.security.NoSuchAlgorithmException;
22+
import java.security.InvalidKeyException;
23+
import java.util.Map;
24+
import java.util.HashMap;
25+
import org.xmlpull.v1.XmlPullParserException;
26+
27+
import io.minio.MinioClient;
28+
import io.minio.CopyConditions;
29+
import io.minio.errors.MinioException;
30+
31+
public class CopyObjectMetadata {
32+
/**
33+
* MinioClient.copyObject() example.
34+
*/
35+
public static void main(String[] args)
36+
throws IOException, NoSuchAlgorithmException, InvalidKeyException, XmlPullParserException {
37+
try {
38+
/* play.minio.io for test and development. */
39+
MinioClient minioClient = new MinioClient("https://play.minio.io:9000", "Q3AM3UQ867SPQQA43P2F",
40+
"zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG");
41+
42+
/* Amazon S3: */
43+
// MinioClient minioClient = new MinioClient("https://s3.amazonaws.com", "YOUR-ACCESSKEYID",
44+
// "YOUR-SECRETACCESSKEY");
45+
46+
// Create some content for the object.
47+
StringBuilder builder = new StringBuilder();
48+
for (int i = 0; i < 1000; i++) {
49+
builder.append("Sphinx of black quartz, judge my vow: Used by Adobe InDesign to display font samples. ");
50+
builder.append("(29 letters)\n");
51+
builder.append("Jackdaws love my big sphinx of quartz: Similarly, used by Windows XP for some fonts. ");
52+
builder.append("(31 letters)\n");
53+
builder.append("Pack my box with five dozen liquor jugs: According to Wikipedia, this one is used on ");
54+
builder.append("NASAs Space Shuttle. (32 letters)\n");
55+
builder.append("The quick onyx goblin jumps over the lazy dwarf: Flavor text from an Unhinged Magic Card. ");
56+
builder.append("(39 letters)\n");
57+
builder.append("How razorback-jumping frogs can level six piqued gymnasts!: Not going to win any brevity ");
58+
builder.append("awards at 49 letters long, but old-time Mac users may recognize it.\n");
59+
builder.append("Cozy lummox gives smart squid who asks for job pen: A 41-letter tester sentence for Mac ");
60+
builder.append("computers after System 7.\n");
61+
builder.append("A few others we like: Amazingly few discotheques provide jukeboxes; Now fax quiz Jack! my ");
62+
builder.append("brave ghost pled; Watch Jeopardy!, Alex Trebeks fun TV quiz game.\n");
63+
builder.append("---\n");
64+
}
65+
66+
// Create a InputStream for object upload.
67+
ByteArrayInputStream bais = new ByteArrayInputStream(builder.toString().getBytes("UTF-8"));
68+
69+
// Create object 'my-objectname' in 'my-bucketname' with content from the input stream.
70+
minioClient.putObject("my-bucketname", "my-objectname", bais, bais.available(), "application/octet-stream");
71+
bais.close();
72+
System.out.println("my-objectname is uploaded successfully");
73+
74+
CopyConditions copyConditions = new CopyConditions();
75+
// Replace metadata on destination object with custom metadata.
76+
copyConditions.setReplaceMetadataDirective();
77+
78+
Map<String, String> metadata = new HashMap<>();
79+
metadata.put("Content-Type", "application/javascript");
80+
81+
minioClient.copyObject("my-bucketname", "my-objectname", "my-destbucketname",
82+
"my-objectname-copy", copyConditions, metadata);
83+
System.out.println("my-objectname-copy copied to my-destbucketname successfully");
84+
} catch (MinioException e) {
85+
System.out.println("Error occurred: " + e);
86+
}
87+
}
88+
}

functional/FunctionalTest.java

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ public static void listObject_test5() throws Exception {
674674
int i;
675675
int objCount = 1050;
676676

677-
System.out.println("Test: recursive: listObjects(final String bucketName, final String prefix"
677+
System.out.println("Test: recursive: listObjects(final String bucketName, final String prefix"
678678
+ ", final boolean recursive)");
679679
String[] objectNames = new String[objCount];
680680

@@ -692,7 +692,7 @@ public static void listObject_test5() throws Exception {
692692

693693
// Check the number of uploaded objects
694694
if (i != objCount) {
695-
throw new Exception("[FAILED] Test: recursive: listObject_test5(), number of items, expected: "
695+
throw new Exception("[FAILED] Test: recursive: listObject_test5(), number of items, expected: "
696696
+ objCount + ", found: " + i);
697697
}
698698

@@ -1390,6 +1390,44 @@ public static void copyObject_test7() throws Exception {
13901390
client.removeBucket(destBucketName);
13911391
}
13921392

1393+
/**
1394+
* Test: copyObject(String bucketName, String objectName, String destBucketName,
1395+
* CopyConditions copyConditions, Map metadata) replace
1396+
* object metadata.
1397+
*/
1398+
public static void copyObject_test8() throws Exception {
1399+
System.out.println("Test: copyObject(String bucketName, String objectName, String destBucketName,"
1400+
+ "CopyConditions copyConditions, Map<String, String> metadata)"
1401+
+ " replace object metadata");
1402+
String filename = createFile(3 * MB);
1403+
client.putObject(bucketName, filename, filename, "application/octet-stream");
1404+
Files.delete(Paths.get(filename));
1405+
1406+
CopyConditions copyConditions = new CopyConditions();
1407+
copyConditions.setReplaceMetadataDirective();
1408+
1409+
Map<String, String> metadata = new HashMap<>();
1410+
metadata.put("Content-Type", customContentType);
1411+
1412+
try {
1413+
client.copyObject(bucketName, filename, bucketName, filename,
1414+
copyConditions, metadata);
1415+
} catch (ErrorResponseException e) {
1416+
// File should not be copied as object was modified after date set in copyConditions.
1417+
if (!e.errorResponse().code().equals("PreconditionFailed")) {
1418+
throw e;
1419+
}
1420+
}
1421+
1422+
ObjectStat objectStat = client.statObject(bucketName, filename);
1423+
if (!customContentType.equals(objectStat.contentType())) {
1424+
throw new Exception("[FAILED] Test: copyObject(String bucketName, String objectName, String destBucketName, "
1425+
+ "CopyConditions copyConditions, Map<String, String> metadata)");
1426+
}
1427+
1428+
client.removeObject(bucketName, filename);
1429+
}
1430+
13931431
/**
13941432
* Test: setBucketNotification(String bucketName, NotificationConfiguration notificationConfiguration).
13951433
*/
@@ -1605,6 +1643,7 @@ public static void runTests() throws Exception {
16051643
copyObject_test5();
16061644
copyObject_test6();
16071645
copyObject_test7();
1646+
copyObject_test8();
16081647

16091648
threadedPutObject();
16101649

0 commit comments

Comments
 (0)