kopia lustrzana https://github.com/gaul/s3proxy
Add partial support for list objects v2
Not supporting fetch-owner until jclouds adds support for this. Several applications like AWS CLI now require this RPC. Fixes #168.pull/293/head
rodzic
4698a2a913
commit
3fed3941f1
|
@ -105,7 +105,6 @@ S3Proxy has broad compatibility with the S3 API, however, it does not support:
|
||||||
* bucket logging
|
* bucket logging
|
||||||
* [CORS bucket operations](https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html#how-do-i-enable-cors) like getting or setting the CORS configuration for a bucket. S3Proxy only supports a static configuration (see below).
|
* [CORS bucket operations](https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html#how-do-i-enable-cors) like getting or setting the CORS configuration for a bucket. S3Proxy only supports a static configuration (see below).
|
||||||
* hosting static websites
|
* hosting static websites
|
||||||
* list objects v2, see [#168](https://github.com/gaul/s3proxy/issues/168)
|
|
||||||
* object server-side encryption
|
* object server-side encryption
|
||||||
* object tagging
|
* object tagging
|
||||||
* object versioning, see [#74](https://github.com/gaul/s3proxy/issues/74)
|
* object versioning, see [#74](https://github.com/gaul/s3proxy/issues/74)
|
||||||
|
|
|
@ -143,7 +143,6 @@ public class S3ProxyHandler {
|
||||||
"cors",
|
"cors",
|
||||||
"inventory",
|
"inventory",
|
||||||
"lifecycle",
|
"lifecycle",
|
||||||
"list-type",
|
|
||||||
"logging",
|
"logging",
|
||||||
"metrics",
|
"metrics",
|
||||||
"notification",
|
"notification",
|
||||||
|
@ -1316,7 +1315,27 @@ public class S3ProxyHandler {
|
||||||
if (prefix != null && !prefix.isEmpty()) {
|
if (prefix != null && !prefix.isEmpty()) {
|
||||||
options.prefix(prefix);
|
options.prefix(prefix);
|
||||||
}
|
}
|
||||||
String marker = request.getParameter("marker");
|
|
||||||
|
boolean isListV2 = false;
|
||||||
|
String marker;
|
||||||
|
String listType = request.getParameter("list-type");
|
||||||
|
String continuationToken = request.getParameter("continuation-token");
|
||||||
|
String startAfter = request.getParameter("start-after");
|
||||||
|
if (listType == null) {
|
||||||
|
marker = request.getParameter("marker");
|
||||||
|
} else if (listType.equals("2")) {
|
||||||
|
isListV2 = true;
|
||||||
|
if (continuationToken != null && startAfter != null) {
|
||||||
|
throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT);
|
||||||
|
}
|
||||||
|
if (continuationToken != null) {
|
||||||
|
marker = continuationToken;
|
||||||
|
} else {
|
||||||
|
marker = startAfter;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new S3Exception(S3ErrorCode.NOT_IMPLEMENTED);
|
||||||
|
}
|
||||||
if (marker != null) {
|
if (marker != null) {
|
||||||
if (Quirks.OPAQUE_MARKERS.contains(blobStoreType)) {
|
if (Quirks.OPAQUE_MARKERS.contains(blobStoreType)) {
|
||||||
String realMarker = lastKeyToMarker.getIfPresent(
|
String realMarker = lastKeyToMarker.getIfPresent(
|
||||||
|
@ -1327,6 +1346,12 @@ public class S3ProxyHandler {
|
||||||
}
|
}
|
||||||
options.afterMarker(marker);
|
options.afterMarker(marker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String fetchOwner = request.getParameter("fetch-owner");
|
||||||
|
if (fetchOwner != null && !fetchOwner.equals("false")) {
|
||||||
|
throw new S3Exception(S3ErrorCode.NOT_IMPLEMENTED);
|
||||||
|
}
|
||||||
|
|
||||||
int maxKeys = 1000;
|
int maxKeys = 1000;
|
||||||
String maxKeysString = request.getParameter("max-keys");
|
String maxKeysString = request.getParameter("max-keys");
|
||||||
if (maxKeysString != null) {
|
if (maxKeysString != null) {
|
||||||
|
@ -1372,11 +1397,26 @@ public class S3ProxyHandler {
|
||||||
|
|
||||||
writeSimpleElement(xml, "MaxKeys", String.valueOf(maxKeys));
|
writeSimpleElement(xml, "MaxKeys", String.valueOf(maxKeys));
|
||||||
|
|
||||||
if (marker == null) {
|
if (!isListV2) {
|
||||||
xml.writeEmptyElement("Marker");
|
if (marker == null) {
|
||||||
|
xml.writeEmptyElement("Marker");
|
||||||
|
} else {
|
||||||
|
writeSimpleElement(xml, "Marker", encodeBlob(
|
||||||
|
encodingType, marker));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
writeSimpleElement(xml, "Marker", encodeBlob(
|
if (continuationToken == null) {
|
||||||
encodingType, marker));
|
xml.writeEmptyElement("ContinuationToken");
|
||||||
|
} else {
|
||||||
|
writeSimpleElement(xml, "ContinuationToken", encodeBlob(
|
||||||
|
encodingType, continuationToken));
|
||||||
|
}
|
||||||
|
if (startAfter == null) {
|
||||||
|
xml.writeEmptyElement("StartAfter");
|
||||||
|
} else {
|
||||||
|
writeSimpleElement(xml, "StartAfter", encodeBlob(
|
||||||
|
encodingType, startAfter));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delimiter != null) {
|
if (delimiter != null) {
|
||||||
|
@ -1391,8 +1431,9 @@ public class S3ProxyHandler {
|
||||||
String nextMarker = set.getNextMarker();
|
String nextMarker = set.getNextMarker();
|
||||||
if (nextMarker != null) {
|
if (nextMarker != null) {
|
||||||
writeSimpleElement(xml, "IsTruncated", "true");
|
writeSimpleElement(xml, "IsTruncated", "true");
|
||||||
writeSimpleElement(xml, "NextMarker", encodeBlob(
|
writeSimpleElement(xml,
|
||||||
encodingType, nextMarker));
|
isListV2 ? "NextContinuationToken" : "NextMarker",
|
||||||
|
encodeBlob(encodingType, nextMarker));
|
||||||
if (Quirks.OPAQUE_MARKERS.contains(blobStoreType)) {
|
if (Quirks.OPAQUE_MARKERS.contains(blobStoreType)) {
|
||||||
lastKeyToMarker.put(Maps.immutableEntry(containerName,
|
lastKeyToMarker.put(Maps.immutableEntry(containerName,
|
||||||
Iterables.getLast(set).getName()), nextMarker);
|
Iterables.getLast(set).getName()), nextMarker);
|
||||||
|
|
|
@ -70,6 +70,8 @@ import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest;
|
||||||
import com.amazonaws.services.s3.model.InitiateMultipartUploadResult;
|
import com.amazonaws.services.s3.model.InitiateMultipartUploadResult;
|
||||||
import com.amazonaws.services.s3.model.ListMultipartUploadsRequest;
|
import com.amazonaws.services.s3.model.ListMultipartUploadsRequest;
|
||||||
import com.amazonaws.services.s3.model.ListObjectsRequest;
|
import com.amazonaws.services.s3.model.ListObjectsRequest;
|
||||||
|
import com.amazonaws.services.s3.model.ListObjectsV2Request;
|
||||||
|
import com.amazonaws.services.s3.model.ListObjectsV2Result;
|
||||||
import com.amazonaws.services.s3.model.ListPartsRequest;
|
import com.amazonaws.services.s3.model.ListPartsRequest;
|
||||||
import com.amazonaws.services.s3.model.MultipartUploadListing;
|
import com.amazonaws.services.s3.model.MultipartUploadListing;
|
||||||
import com.amazonaws.services.s3.model.ObjectListing;
|
import com.amazonaws.services.s3.model.ObjectListing;
|
||||||
|
@ -904,6 +906,52 @@ public final class AwsSdkTest {
|
||||||
.isEqualTo("blob2");
|
.isEqualTo("blob2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBlobListV2() throws Exception {
|
||||||
|
ObjectMetadata metadata = new ObjectMetadata();
|
||||||
|
metadata.setContentLength(BYTE_SOURCE.size());
|
||||||
|
for (int i = 1; i < 5; ++i) {
|
||||||
|
client.putObject(containerName, String.valueOf(i),
|
||||||
|
BYTE_SOURCE.openStream(), metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
ListObjectsV2Result result = client.listObjectsV2(
|
||||||
|
new ListObjectsV2Request()
|
||||||
|
.withBucketName(containerName)
|
||||||
|
.withMaxKeys(1)
|
||||||
|
.withStartAfter("1"));
|
||||||
|
assertThat(result.getContinuationToken()).isEmpty();
|
||||||
|
assertThat(result.getStartAfter()).isEqualTo("1");
|
||||||
|
assertThat(result.getNextContinuationToken()).isEqualTo("2");
|
||||||
|
assertThat(result.isTruncated()).isTrue();
|
||||||
|
assertThat(result.getObjectSummaries()).hasSize(1);
|
||||||
|
assertThat(result.getObjectSummaries().get(0).getKey()).isEqualTo("2");
|
||||||
|
|
||||||
|
result = client.listObjectsV2(
|
||||||
|
new ListObjectsV2Request()
|
||||||
|
.withBucketName(containerName)
|
||||||
|
.withMaxKeys(1)
|
||||||
|
.withContinuationToken(result.getNextContinuationToken()));
|
||||||
|
assertThat(result.getContinuationToken()).isEqualTo("2");
|
||||||
|
assertThat(result.getStartAfter()).isEmpty();
|
||||||
|
assertThat(result.getNextContinuationToken()).isEqualTo("3");
|
||||||
|
assertThat(result.isTruncated()).isTrue();
|
||||||
|
assertThat(result.getObjectSummaries()).hasSize(1);
|
||||||
|
assertThat(result.getObjectSummaries().get(0).getKey()).isEqualTo("3");
|
||||||
|
|
||||||
|
result = client.listObjectsV2(
|
||||||
|
new ListObjectsV2Request()
|
||||||
|
.withBucketName(containerName)
|
||||||
|
.withMaxKeys(1)
|
||||||
|
.withContinuationToken(result.getNextContinuationToken()));
|
||||||
|
assertThat(result.getContinuationToken()).isEqualTo("3");
|
||||||
|
assertThat(result.getStartAfter()).isEmpty();
|
||||||
|
assertThat(result.getNextContinuationToken()).isNull();
|
||||||
|
assertThat(result.isTruncated()).isFalse();
|
||||||
|
assertThat(result.getObjectSummaries()).hasSize(1);
|
||||||
|
assertThat(result.getObjectSummaries().get(0).getKey()).isEqualTo("4");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBlobMetadata() throws Exception {
|
public void testBlobMetadata() throws Exception {
|
||||||
String blobName = "blob";
|
String blobName = "blob";
|
||||||
|
|
Ładowanie…
Reference in New Issue