kopia lustrzana https://github.com/gaul/s3proxy
Add support for listing multipart uploads
Only supported in Azure, B2, and S3. Fixes #118.pull/139/merge
rodzic
72138e1278
commit
187eb065aa
|
@ -61,6 +61,16 @@ final class Quirks {
|
||||||
"azureblob"
|
"azureblob"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static final Set<String> NO_LIST_MULTIPART_UPLOADS = ImmutableSet.of(
|
||||||
|
"atmos",
|
||||||
|
"filesystem",
|
||||||
|
"google-cloud-storage",
|
||||||
|
"openstack-swift",
|
||||||
|
"rackspace-cloudfiles-uk",
|
||||||
|
"rackspace-cloudfiles-us",
|
||||||
|
"transient"
|
||||||
|
);
|
||||||
|
|
||||||
/** Blobstores which do not allow listing zero keys. */
|
/** Blobstores which do not allow listing zero keys. */
|
||||||
static final Set<String> NO_LIST_ZERO_KEYS = ImmutableSet.of(
|
static final Set<String> NO_LIST_ZERO_KEYS = ImmutableSet.of(
|
||||||
"atmos",
|
"atmos",
|
||||||
|
|
|
@ -501,8 +501,8 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
handleContainerLocation(response, blobStore, path[1]);
|
handleContainerLocation(response, blobStore, path[1]);
|
||||||
return;
|
return;
|
||||||
} else if ("".equals(request.getParameter("uploads"))) {
|
} else if ("".equals(request.getParameter("uploads"))) {
|
||||||
handleListMultipartUploads(response, blobStore,
|
handleListMultipartUploads(request, response, blobStore,
|
||||||
uploadId);
|
path[1]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handleBlobList(request, response, blobStore, path[1]);
|
handleBlobList(request, response, blobStore, path[1]);
|
||||||
|
@ -974,11 +974,64 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleListMultipartUploads(HttpServletResponse response,
|
private void handleListMultipartUploads(HttpServletRequest request,
|
||||||
BlobStore blobStore, String uploadId)
|
HttpServletResponse response, BlobStore blobStore,
|
||||||
throws IOException, S3Exception {
|
String container) throws IOException, S3Exception {
|
||||||
// TODO: list all blobs starting with uploadId
|
if (request.getParameter("delimiter") != null ||
|
||||||
throw new S3Exception(S3ErrorCode.NOT_IMPLEMENTED);
|
request.getParameter("prefix") != null ||
|
||||||
|
request.getParameter("max-uploads") != null ||
|
||||||
|
request.getParameter("key-marker") != null ||
|
||||||
|
request.getParameter("upload-id-marker") != null) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<MultipartUpload> uploads = blobStore.listMultipartUploads(
|
||||||
|
container);
|
||||||
|
|
||||||
|
try (Writer writer = response.getWriter()) {
|
||||||
|
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
|
||||||
|
writer);
|
||||||
|
xml.writeStartDocument();
|
||||||
|
xml.writeStartElement("ListMultipartUploadsResult");
|
||||||
|
xml.writeDefaultNamespace(AWS_XMLNS);
|
||||||
|
|
||||||
|
writeSimpleElement(xml, "Bucket", container);
|
||||||
|
|
||||||
|
// TODO: bogus values
|
||||||
|
xml.writeEmptyElement("KeyMarker");
|
||||||
|
xml.writeEmptyElement("UploadIdMarker");
|
||||||
|
xml.writeEmptyElement("NextKeyMarker");
|
||||||
|
xml.writeEmptyElement("NextUploadIdMarker");
|
||||||
|
xml.writeEmptyElement("Delimiter");
|
||||||
|
xml.writeEmptyElement("Prefix");
|
||||||
|
writeSimpleElement(xml, "MaxUploads", "1000");
|
||||||
|
writeSimpleElement(xml, "IsTruncated", "false");
|
||||||
|
|
||||||
|
for (MultipartUpload upload : uploads) {
|
||||||
|
xml.writeStartElement("Upload");
|
||||||
|
|
||||||
|
writeSimpleElement(xml, "Key", upload.blobName());
|
||||||
|
writeSimpleElement(xml, "UploadId", upload.id());
|
||||||
|
writeInitiatorStanza(xml);
|
||||||
|
writeOwnerStanza(xml);
|
||||||
|
writeSimpleElement(xml, "StorageClass", "STANDARD");
|
||||||
|
|
||||||
|
// TODO: bogus value
|
||||||
|
writeSimpleElement(xml, "Initiated",
|
||||||
|
blobStore.getContext().utils().date()
|
||||||
|
.iso8601DateFormat(new Date()));
|
||||||
|
|
||||||
|
xml.writeEndElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
xml.writeEmptyElement("CommonPrefixes");
|
||||||
|
|
||||||
|
xml.writeEndElement();
|
||||||
|
|
||||||
|
xml.flush();
|
||||||
|
} catch (XMLStreamException xse) {
|
||||||
|
throw new IOException(xse);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleContainerExists(HttpServletResponse response,
|
private void handleContainerExists(HttpServletResponse response,
|
||||||
|
@ -1923,18 +1976,8 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
writeSimpleElement(xml, "Key", encodeBlob(
|
writeSimpleElement(xml, "Key", encodeBlob(
|
||||||
encodingType, blobName));
|
encodingType, blobName));
|
||||||
writeSimpleElement(xml, "UploadId", uploadId);
|
writeSimpleElement(xml, "UploadId", uploadId);
|
||||||
|
writeInitiatorStanza(xml);
|
||||||
// TODO: bogus values
|
|
||||||
xml.writeStartElement("Initiator");
|
|
||||||
|
|
||||||
writeSimpleElement(xml, "ID", FAKE_INITIATOR_ID);
|
|
||||||
writeSimpleElement(xml, "DisplayName",
|
|
||||||
FAKE_INITIATOR_DISPLAY_NAME);
|
|
||||||
|
|
||||||
xml.writeEndElement();
|
|
||||||
|
|
||||||
writeOwnerStanza(xml);
|
writeOwnerStanza(xml);
|
||||||
|
|
||||||
writeSimpleElement(xml, "StorageClass", "STANDARD");
|
writeSimpleElement(xml, "StorageClass", "STANDARD");
|
||||||
|
|
||||||
// TODO: pagination
|
// TODO: pagination
|
||||||
|
@ -2604,6 +2647,18 @@ final class S3ProxyHandler extends AbstractHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: bogus values
|
||||||
|
private static void writeInitiatorStanza(XMLStreamWriter xml)
|
||||||
|
throws XMLStreamException {
|
||||||
|
xml.writeStartElement("Initiator");
|
||||||
|
|
||||||
|
writeSimpleElement(xml, "ID", FAKE_INITIATOR_ID);
|
||||||
|
writeSimpleElement(xml, "DisplayName",
|
||||||
|
FAKE_INITIATOR_DISPLAY_NAME);
|
||||||
|
|
||||||
|
xml.writeEndElement();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: bogus values
|
// TODO: bogus values
|
||||||
private static void writeOwnerStanza(XMLStreamWriter xml)
|
private static void writeOwnerStanza(XMLStreamWriter xml)
|
||||||
throws XMLStreamException {
|
throws XMLStreamException {
|
||||||
|
|
|
@ -123,6 +123,14 @@ public final class JcloudsS3BlobIntegrationLiveTest
|
||||||
super.testCopyIfNoneMatchNegative();
|
super.testCopyIfNoneMatchNegative();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void testListMultipartUploads() throws Exception {
|
||||||
|
if (Quirks.NO_LIST_MULTIPART_UPLOADS.contains(blobStoreType)) {
|
||||||
|
throw new SkipException("list multipart uploads not supported");
|
||||||
|
}
|
||||||
|
super.testListMultipartUploads();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void checkCacheControl(Blob blob, String cacheControl) {
|
protected void checkCacheControl(Blob blob, String cacheControl) {
|
||||||
if (!Quirks.NO_CACHE_CONTROL_SUPPORT.contains(blobStoreType)) {
|
if (!Quirks.NO_CACHE_CONTROL_SUPPORT.contains(blobStoreType)) {
|
||||||
|
|
|
@ -162,6 +162,14 @@ public final class JcloudsS3ClientLiveTest extends S3ClientLiveTest {
|
||||||
super.testUpdateObjectCannedACL();
|
super.testUpdateObjectCannedACL();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void testListMultipartUploads() throws Exception {
|
||||||
|
if (Quirks.NO_LIST_MULTIPART_UPLOADS.contains(blobStoreType)) {
|
||||||
|
throw new SkipException("list multipart uploads not supported");
|
||||||
|
}
|
||||||
|
super.testListMultipartUploads();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void assertCacheControl(S3Object newObject, String string) {
|
protected void assertCacheControl(S3Object newObject, String string) {
|
||||||
if (Quirks.NO_CACHE_CONTROL_SUPPORT.contains(blobStoreType)) {
|
if (Quirks.NO_CACHE_CONTROL_SUPPORT.contains(blobStoreType)) {
|
||||||
|
|
Ładowanie…
Reference in New Issue