Add native object copy support

Fixes #46.
pull/53/head
Andrew Gaul 2015-04-02 00:31:55 -07:00
rodzic 35b37c34a9
commit 7b937c1571
3 zmienionych plików z 171 dodań i 48 usunięć

Wyświetl plik

@ -235,7 +235,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jclouds.version>1.9.0</jclouds.version>
<jclouds.version>1.9.1-SNAPSHOT</jclouds.version>
</properties>
<prerequisites>

Wyświetl plik

@ -83,6 +83,7 @@ import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.ContainerAccess;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.blobstore.options.CopyOptions;
import org.jclouds.blobstore.options.CreateContainerOptions;
import org.jclouds.blobstore.options.GetOptions;
import org.jclouds.blobstore.options.ListContainerOptions;
@ -91,6 +92,7 @@ import org.jclouds.domain.Location;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException;
import org.jclouds.io.ContentMetadata;
import org.jclouds.io.ContentMetadataBuilder;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.util.Throwables2;
import org.slf4j.Logger;
@ -1093,58 +1095,65 @@ final class S3ProxyHandler extends AbstractHandler {
throw new S3Exception(S3ErrorCode.INVALID_REQUEST);
}
Blob blob = blobStore.getBlob(sourceContainerName, sourceBlobName);
if (blob == null) {
throw new S3Exception(S3ErrorCode.NO_SUCH_KEY);
CopyOptions.Builder options = CopyOptions.builder();
if (replaceMetadata) {
ContentMetadataBuilder contentMetadata =
ContentMetadataBuilder.create();
ImmutableMap.Builder<String, String> userMetadata =
ImmutableMap.builder();
for (String headerName : Collections.list(
request.getHeaderNames())) {
String headerValue = Strings.nullToEmpty(request.getHeader(
headerName));
if (headerName.equalsIgnoreCase(
HttpHeaders.CONTENT_DISPOSITION)) {
contentMetadata.contentDisposition(headerValue);
} else if (headerName.equalsIgnoreCase(
HttpHeaders.CONTENT_ENCODING)) {
contentMetadata.contentEncoding(headerValue);
} else if (headerName.equalsIgnoreCase(
HttpHeaders.CONTENT_LANGUAGE)) {
contentMetadata.contentLanguage(headerValue);
} else if (headerName.equalsIgnoreCase(
HttpHeaders.CONTENT_TYPE)) {
contentMetadata.contentType(headerValue);
} else if (headerName.toLowerCase().startsWith(
USER_METADATA_PREFIX)) {
userMetadata.put(
headerName.substring(USER_METADATA_PREFIX.length()),
headerValue);
}
// TODO: Expires
}
options.contentMetadata(contentMetadata.build());
options.userMetadata(userMetadata.build());
}
try (InputStream is = blob.getPayload().openStream()) {
ContentMetadata metadata = blob.getMetadata().getContentMetadata();
long contentLength = metadata.getContentLength();
BlobBuilder.PayloadBlobBuilder builder = blobStore
.blobBuilder(destBlobName)
.payload(is)
.contentLength(contentLength);
if (replaceMetadata) {
addContentMetdataFromHttpRequest(builder, request);
} else {
builder.contentDisposition(metadata.getContentDisposition())
.contentEncoding(metadata.getContentEncoding())
.contentLanguage(metadata.getContentLanguage())
.contentType(metadata.getContentType())
.userMetadata(blob.getMetadata().getUserMetadata());
}
String eTag = blobStore.copyBlob(
sourceContainerName, sourceBlobName,
destContainerName, destBlobName, options.build());
BlobMetadata blobMetadata = blobStore.blobMetadata(destContainerName,
destBlobName);
try (Writer writer = response.getWriter()) {
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
writer);
xml.writeStartDocument();
xml.writeStartElement("CopyObjectResult");
xml.writeDefaultNamespace(AWS_XMLNS);
PutOptions options = new PutOptions();
String blobStoreType = getBlobStoreType(blobStore);
if (blobStoreType.equals("azureblob") &&
contentLength > 64 * 1024 * 1024) {
options.multipart(true);
}
String eTag = blobStore.putBlob(destContainerName,
builder.build(), options);
Date lastModified = blob.getMetadata().getLastModified();
try (Writer writer = response.getWriter()) {
XMLStreamWriter xml = xmlOutputFactory.createXMLStreamWriter(
writer);
xml.writeStartDocument();
xml.writeStartElement("CopyObjectResult");
xml.writeDefaultNamespace(AWS_XMLNS);
xml.writeStartElement("LastModified");
xml.writeCharacters(blobStore.getContext().utils().date()
.iso8601DateFormat(blobMetadata.getLastModified()));
xml.writeEndElement();
xml.writeStartElement("LastModified");
xml.writeCharacters(blobStore.getContext().utils().date()
.iso8601DateFormat(lastModified));
xml.writeEndElement();
xml.writeStartElement("ETag");
xml.writeCharacters("\"" + eTag + "\"");
xml.writeEndElement();
xml.writeStartElement("ETag");
xml.writeCharacters("\"" + eTag + "\"");
xml.writeEndElement();
xml.writeEndElement();
xml.flush();
} catch (XMLStreamException xse) {
throw new IOException(xse);
}
xml.writeEndElement();
xml.flush();
} catch (XMLStreamException xse) {
throw new IOException(xse);
}
}

Wyświetl plik

@ -20,6 +20,8 @@ import static org.assertj.core.api.Assertions.assertThat;
import java.io.InputStream;
import java.net.URI;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
@ -27,6 +29,7 @@ import javax.servlet.http.HttpServletResponse;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteSource;
import com.google.common.io.Resources;
@ -44,10 +47,13 @@ import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.blobstore.options.CopyOptions;
import org.jclouds.blobstore.options.ListContainerOptions;
import org.jclouds.blobstore.options.PutOptions;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.io.ContentMetadata;
import org.jclouds.io.ContentMetadataBuilder;
import org.jclouds.io.Payload;
import org.jclouds.io.payloads.ByteSourcePayload;
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
@ -388,6 +394,114 @@ public final class S3ProxyTest {
new PutOptions().multipart(true));
}
@Test
public void testCopyObjectPreserveMetadata() throws Exception {
String fromName = "from-name";
String toName = "to-name";
ByteSource byteSource = ByteSource.wrap(new byte[42]);
String contentDisposition = "attachment; filename=old.jpg";
String contentEncoding = "gzip";
String contentLanguage = "en";
String contentType = "audio/ogg";
Date expires = new Date(1000);
Map<String, String> userMetadata = ImmutableMap.of(
"key1", "value1",
"key2", "value2");
Blob fromBlob = s3BlobStore.blobBuilder(fromName)
.payload(byteSource)
.contentLength(byteSource.size())
.contentDisposition(contentDisposition)
.contentEncoding(contentEncoding)
.contentLanguage(contentLanguage)
.contentType(contentType)
.expires(expires)
.userMetadata(userMetadata)
.build();
s3BlobStore.putBlob(containerName, fromBlob);
s3BlobStore.copyBlob(containerName, fromName, containerName, toName,
CopyOptions.NONE);
Blob toBlob = s3BlobStore.getBlob(containerName, toName);
try (InputStream actual = toBlob.getPayload().openStream();
InputStream expected = byteSource.openStream()) {
assertThat(actual).hasContentEqualTo(expected);
}
ContentMetadata contentMetadata =
toBlob.getMetadata().getContentMetadata();
assertThat(contentMetadata.getContentDisposition()).isEqualTo(
contentDisposition);
assertThat(contentMetadata.getContentEncoding()).isEqualTo(
contentEncoding);
assertThat(contentMetadata.getContentLanguage()).isEqualTo(
contentLanguage);
assertThat(contentMetadata.getContentType()).isEqualTo(
contentType);
// TODO: expires
assertThat(toBlob.getMetadata().getUserMetadata()).isEqualTo(
userMetadata);
}
@Test
public void testCopyObjectReplaceMetadata() throws Exception {
String fromName = "from-name";
String toName = "to-name";
ByteSource byteSource = ByteSource.wrap(new byte[42]);
Blob fromBlob = s3BlobStore.blobBuilder(fromName)
.payload(byteSource)
.contentLength(byteSource.size())
.contentDisposition("attachment; filename=old.jpg")
.contentEncoding("compress")
.contentLanguage("en")
.contentType("audio/ogg")
.expires(new Date(1000))
.userMetadata(ImmutableMap.of(
"key1", "value1",
"key2", "value2"))
.build();
s3BlobStore.putBlob(containerName, fromBlob);
String contentDisposition = "attachment; filename=new.jpg";
String contentEncoding = "gzip";
String contentLanguage = "fr";
String contentType = "audio/mp4";
Date expires = new Date(2000);
ContentMetadata contentMetadata = ContentMetadataBuilder.create()
.contentDisposition(contentDisposition)
.contentEncoding(contentEncoding)
.contentLanguage(contentLanguage)
.contentType(contentType)
.expires(expires)
.build();
Map<String, String> userMetadata = ImmutableMap.of(
"key3", "value3",
"key4", "value4");
s3BlobStore.copyBlob(containerName, fromName, containerName, toName,
CopyOptions.builder()
.contentMetadata(contentMetadata)
.userMetadata(userMetadata)
.build());
Blob toBlob = s3BlobStore.getBlob(containerName, toName);
try (InputStream actual = toBlob.getPayload().openStream();
InputStream expected = byteSource.openStream()) {
assertThat(actual).hasContentEqualTo(expected);
}
ContentMetadata toContentMetadata =
toBlob.getMetadata().getContentMetadata();
assertThat(toContentMetadata.getContentDisposition()).isEqualTo(
contentDisposition);
assertThat(toContentMetadata.getContentEncoding()).isEqualTo(
contentEncoding);
assertThat(toContentMetadata.getContentLanguage()).isEqualTo(
contentLanguage);
assertThat(toContentMetadata.getContentType()).isEqualTo(
contentType);
// TODO: expires
assertThat(toBlob.getMetadata().getUserMetadata()).isEqualTo(
userMetadata);
}
@Test
public void testUnknownParameter() throws Exception {
S3Client s3Client = s3Context.unwrapApi(S3Client.class);