diff --git a/pom.xml b/pom.xml
index 63018a1b..97bb3550 100644
--- a/pom.xml
+++ b/pom.xml
@@ -235,7 +235,7 @@
UTF-8
- 1.9.0
+ 1.9.1-SNAPSHOT
diff --git a/src/main/java/org/gaul/s3proxy/S3ProxyHandler.java b/src/main/java/org/gaul/s3proxy/S3ProxyHandler.java
index 6af61278..91f055ad 100644
--- a/src/main/java/org/gaul/s3proxy/S3ProxyHandler.java
+++ b/src/main/java/org/gaul/s3proxy/S3ProxyHandler.java
@@ -83,6 +83,7 @@
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.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 @@ private void handleCopyBlob(HttpServletRequest request,
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 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(lastModified));
- xml.writeEndElement();
+ xml.writeStartElement("LastModified");
+ xml.writeCharacters(blobStore.getContext().utils().date()
+ .iso8601DateFormat(blobMetadata.getLastModified()));
+ 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);
}
}
diff --git a/src/test/java/org/gaul/s3proxy/S3ProxyTest.java b/src/test/java/org/gaul/s3proxy/S3ProxyTest.java
index bee521bb..f45d7694 100644
--- a/src/test/java/org/gaul/s3proxy/S3ProxyTest.java
+++ b/src/test/java/org/gaul/s3proxy/S3ProxyTest.java
@@ -20,6 +20,8 @@
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 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.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 void testMultipartUpload() throws Exception {
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 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 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);