Skip to content

Commit ba04d55

Browse files
balamuruganaharshavardhana
authored andcommitted
api: add new API to delete multiple objects. (#553)
* api: add new API to delete multiple objects. This patch adds new API public Iterable<Result<DeleteError>> removeObject(final String bucketName, final Iterable<String> objectNames) which uses "Delete Multiple Objects" ReST API (http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html). Fixes #467 * Add example and docs.
1 parent 832d0e4 commit ba04d55

File tree

12 files changed

+553
-14
lines changed

12 files changed

+553
-14
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ The full API Reference is available here.
137137
* [GetObject.Java](https://github.com/minio/minio-java/tree/master/examples/GetObject.java)
138138
* [GetPartialObject.java](https://github.com/minio/minio-java/tree/master/examples/GetPartialObject.java)
139139
* [RemoveObject.java](https://github.com/minio/minio-java/tree/master/examples/RemoveObject.java)
140+
* [RemoveObjects.java](https://github.com/minio/minio-java/tree/master/examples/RemoveObjects.java)
140141
* [StatObject.java](https://github.com/minio/minio-java/tree/master/examples/StatObject.java)
141142

142143
#### Full Examples: Presigned Operations

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

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@
4242
import io.minio.messages.CompleteMultipartUpload;
4343
import io.minio.messages.CopyObjectResult;
4444
import io.minio.messages.CreateBucketConfiguration;
45+
import io.minio.messages.DeleteError;
46+
import io.minio.messages.DeleteObject;
47+
import io.minio.messages.DeleteRequest;
48+
import io.minio.messages.DeleteResult;
4549
import io.minio.messages.ErrorResponse;
4650
import io.minio.messages.InitiateMultipartUploadResult;
4751
import io.minio.messages.Item;
@@ -1884,6 +1888,160 @@ public void removeObject(String bucketName, String objectName)
18841888
}
18851889

18861890

1891+
private List<DeleteError> removeObject(String bucketName, List<DeleteObject> objectList)
1892+
throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException,
1893+
InvalidKeyException, NoResponseException, XmlPullParserException, ErrorResponseException,
1894+
InternalException {
1895+
Map<String,String> queryParamMap = new HashMap<>();
1896+
queryParamMap.put("delete", "");
1897+
1898+
DeleteRequest request = new DeleteRequest(objectList);
1899+
HttpResponse response = executePost(bucketName, null, null, queryParamMap, request);
1900+
1901+
String bodyContent = "";
1902+
// Use scanner to read entire body stream to string.
1903+
Scanner scanner = new Scanner(response.body().charStream());
1904+
try {
1905+
scanner.useDelimiter("\\A");
1906+
if (scanner.hasNext()) {
1907+
bodyContent = scanner.next();
1908+
}
1909+
} finally {
1910+
response.body().close();
1911+
scanner.close();
1912+
}
1913+
1914+
List<DeleteError> errorList = null;
1915+
1916+
bodyContent = bodyContent.trim();
1917+
// Check if body content is <Error> message.
1918+
DeleteError error = new DeleteError(new StringReader(bodyContent));
1919+
if (error.code() != null) {
1920+
// As it is <Error> message, add to error list.
1921+
errorList = new LinkedList<DeleteError>();
1922+
errorList.add(error);
1923+
} else {
1924+
// As it is not <Error> message, parse it as <DeleteResult> message.
1925+
DeleteResult result = new DeleteResult(new StringReader(bodyContent));
1926+
errorList = result.errorList();
1927+
}
1928+
1929+
return errorList;
1930+
}
1931+
1932+
1933+
/**
1934+
* Removes multiple objects from a bucket.
1935+
*
1936+
* </p><b>Example:</b><br>
1937+
* <pre>{@code // Create object list for removal.
1938+
* List<String> objectNames = new LinkedList<String>();
1939+
* objectNames.add("my-objectname1");
1940+
* objectNames.add("my-objectname2");
1941+
* objectNames.add("my-objectname3");
1942+
* for (Result<DeleteError> errorResult: minioClient.removeObject("my-bucketname", objectNames)) {
1943+
* DeleteError error = errorResult.get();
1944+
* System.out.println("Failed to remove '" + error.objectName() + "'. Error:" + error.message());
1945+
* } }</pre>
1946+
*
1947+
* @param bucketName Bucket name.
1948+
* @param objectNames List of Object names in the bucket.
1949+
*/
1950+
public Iterable<Result<DeleteError>> removeObject(final String bucketName, final Iterable<String> objectNames) {
1951+
return new Iterable<Result<DeleteError>>() {
1952+
@Override
1953+
public Iterator<Result<DeleteError>> iterator() {
1954+
return new Iterator<Result<DeleteError>>() {
1955+
private Result<DeleteError> error;
1956+
private Iterator<DeleteError> errorIterator;
1957+
private boolean completed = false;
1958+
1959+
private synchronized void populate() {
1960+
List<DeleteError> errorList = null;
1961+
try {
1962+
List<DeleteObject> objectList = new LinkedList<DeleteObject>();
1963+
int i = 0;
1964+
for (String objectName: objectNames) {
1965+
objectList.add(new DeleteObject(objectName));
1966+
i++;
1967+
// Maximum 1000 objects are allowed in a request
1968+
if (i == 1000) {
1969+
break;
1970+
}
1971+
}
1972+
1973+
if (i == 0) {
1974+
return;
1975+
}
1976+
1977+
errorList = removeObject(bucketName, objectList);
1978+
} catch (InvalidBucketNameException | NoSuchAlgorithmException | InsufficientDataException | IOException
1979+
| InvalidKeyException | NoResponseException | XmlPullParserException | ErrorResponseException
1980+
| InternalException e) {
1981+
this.error = new Result<>(null, e);
1982+
} finally {
1983+
if (errorList != null) {
1984+
this.errorIterator = errorList.iterator();
1985+
} else {
1986+
this.errorIterator = new LinkedList<DeleteError>().iterator();
1987+
}
1988+
}
1989+
}
1990+
1991+
@Override
1992+
public boolean hasNext() {
1993+
if (this.completed) {
1994+
return false;
1995+
}
1996+
1997+
if (this.error == null && this.errorIterator == null) {
1998+
populate();
1999+
}
2000+
2001+
if (this.error != null) {
2002+
return true;
2003+
}
2004+
2005+
if (this.errorIterator.hasNext()) {
2006+
return true;
2007+
}
2008+
2009+
this.completed = true;
2010+
return false;
2011+
}
2012+
2013+
@Override
2014+
public Result<DeleteError> next() {
2015+
if (this.completed) {
2016+
throw new NoSuchElementException();
2017+
}
2018+
2019+
if (this.error == null && this.errorIterator == null) {
2020+
populate();
2021+
}
2022+
2023+
if (this.error != null) {
2024+
this.completed = true;
2025+
return this.error;
2026+
}
2027+
2028+
if (this.errorIterator.hasNext()) {
2029+
return new Result<>(this.errorIterator.next(), null);
2030+
}
2031+
2032+
this.completed = true;
2033+
throw new NoSuchElementException();
2034+
}
2035+
2036+
@Override
2037+
public void remove() {
2038+
throw new UnsupportedOperationException();
2039+
}
2040+
};
2041+
}
2042+
};
2043+
}
2044+
18872045
/**
18882046
* Lists object information in given bucket.
18892047
*
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Minio Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2017 Minio, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.minio.messages;
18+
19+
import java.io.IOException;
20+
import java.io.Reader;
21+
22+
import org.xmlpull.v1.XmlPullParserException;
23+
24+
25+
/**
26+
* Helper class to parse Amazon AWS S3 error response XML.
27+
*/
28+
@SuppressWarnings("unused")
29+
public class DeleteError extends ErrorResponse {
30+
/**
31+
* Constructs a new ErrorResponse object by reading given reader stream.
32+
*/
33+
public DeleteError() throws XmlPullParserException {
34+
super();
35+
super.name = "Error";
36+
}
37+
38+
39+
/**
40+
* Constructs a new ErrorResponse object by reading given reader stream.
41+
*/
42+
public DeleteError(Reader reader) throws IOException, XmlPullParserException {
43+
this();
44+
this.parseXml(reader);
45+
}
46+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Minio Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2017 Minio, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.minio.messages;
18+
19+
import org.xmlpull.v1.XmlPullParserException;
20+
21+
import com.google.api.client.util.Key;
22+
23+
24+
/**
25+
* Helper class to create Amazon AWS S3 request XML containing object information for Multiple object deletion.
26+
*/
27+
@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD")
28+
public class DeleteObject extends XmlEntity {
29+
@Key("Key")
30+
private String name;
31+
@Key("VersionId")
32+
private String versionId;
33+
34+
35+
public DeleteObject(String name) throws XmlPullParserException {
36+
this(name, null);
37+
}
38+
39+
40+
/**
41+
* Constructs new delete object for given name and version ID.
42+
*
43+
*/
44+
public DeleteObject(String name, String versionId) throws XmlPullParserException {
45+
super();
46+
super.name = "Object";
47+
48+
this.name = name;
49+
this.versionId = versionId;
50+
}
51+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Minio Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2017 Minio, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.minio.messages;
18+
19+
import java.util.List;
20+
21+
import org.xmlpull.v1.XmlPullParserException;
22+
23+
import com.google.api.client.util.Key;
24+
25+
26+
/**
27+
* Helper class to create Amazon AWS S3 request XML containing information for Multiple object deletion.
28+
*/
29+
@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "URF_UNREAD_FIELD")
30+
public class DeleteRequest extends XmlEntity {
31+
@Key("Quiet")
32+
private boolean quiet;
33+
@Key("Object")
34+
private List<DeleteObject> objectList;
35+
36+
37+
public DeleteRequest(List<DeleteObject> objectList) throws XmlPullParserException {
38+
this(objectList, true);
39+
}
40+
41+
42+
/**
43+
* Constructs new delete request for given object list and quiet flag.
44+
*/
45+
public DeleteRequest(List<DeleteObject> objectList, boolean quiet) throws XmlPullParserException {
46+
super();
47+
super.name = "Delete";
48+
super.namespaceDictionary.set("", "http://s3.amazonaws.com/doc/2006-03-01/");
49+
50+
this.objectList = objectList;
51+
this.quiet = quiet;
52+
}
53+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Minio Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2017 Minio, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.minio.messages;
18+
19+
import java.io.IOException;
20+
import java.io.Reader;
21+
import java.util.LinkedList;
22+
import java.util.List;
23+
24+
import org.xmlpull.v1.XmlPullParserException;
25+
26+
import com.google.api.client.util.Key;
27+
28+
29+
/**
30+
* Helper class to create Amazon AWS S3 request XML containing information for Multiple object deletion.
31+
*/
32+
@SuppressWarnings({"SameParameterValue", "unused"})
33+
public class DeleteResult extends XmlEntity {
34+
@Key("Deleted")
35+
private List<DeletedObject> objectList = new LinkedList<>();
36+
@Key("Error")
37+
private List<DeleteError> errorList = new LinkedList<>();
38+
39+
40+
/**
41+
* Constructs new delete result by parsing content on given reader.
42+
*/
43+
public DeleteResult(Reader reader) throws IOException, XmlPullParserException {
44+
super();
45+
super.name = "DeleteResult";
46+
this.parseXml(reader);
47+
}
48+
49+
50+
public List<DeletedObject> objectList() {
51+
return objectList;
52+
}
53+
54+
55+
public List<DeleteError> errorList() {
56+
return errorList;
57+
}
58+
}

0 commit comments

Comments
 (0)