JCLOUDS-458: Multipart Related Upload

This commit is contained in:
hsbhathiya 2014-08-29 01:45:03 +05:30 committed by Andrew Gaul
parent 0c55612bb1
commit 72e42dbfba
4 changed files with 147 additions and 1 deletions

View File

@ -90,6 +90,12 @@
<artifactId>logback-classic</artifactId> <artifactId>logback-classic</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>1.6.1</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<profiles> <profiles>
<profile> <profile>

View File

@ -0,0 +1,71 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.googlecloudstorage.binders;
import java.util.Map;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import org.jclouds.googlecloudstorage.domain.templates.ObjectTemplate;
import org.jclouds.http.HttpRequest;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
import org.jclouds.io.payloads.MultipartForm;
import org.jclouds.io.payloads.Part;
import org.jclouds.io.payloads.StringPayload;
import org.jclouds.rest.MapBinder;
import com.google.gson.Gson;
import static com.google.common.base.Preconditions.checkNotNull;
public class MultipartUploadBinder implements MapBinder {
private final String BOUNDARY_HEADER = "multipart_boundary";
@Override
public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams)
throws IllegalArgumentException {
ObjectTemplate template = (ObjectTemplate) postParams.get("template");
Payload payload = (Payload) postParams.get("payload");
String contentType = checkNotNull(template.getContentType(), "contentType");
Long length = checkNotNull(template.getSize(), "contetLength");
StringPayload jsonPayload = Payloads.newStringPayload(new Gson().toJson(template));
payload.getContentMetadata().setContentLength(length);
Part jsonPart = Part.create("Metadata", jsonPayload,
new Part.PartOptions().contentType(MediaType.APPLICATION_JSON));
Part mediaPart = Part.create(template.getName(), payload, new Part.PartOptions().contentType(contentType));
MultipartForm compPayload = new MultipartForm(BOUNDARY_HEADER, jsonPart, mediaPart);
request.setPayload(compPayload);
// HeaderPart
request.toBuilder().replaceHeader(HttpHeaders.CONTENT_TYPE, "Multipart/related; boundary= " + BOUNDARY_HEADER)
.build();
return request;
}
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
return request;
}
}

View File

@ -35,6 +35,7 @@ import org.jclouds.Fallbacks.FalseOnNotFoundOr404;
import org.jclouds.Fallbacks.NullOnNotFoundOr404; import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.Fallbacks.TrueOnNotFoundOr404; import org.jclouds.Fallbacks.TrueOnNotFoundOr404;
import org.jclouds.googlecloudstorage.binders.ComposeObjectBinder; import org.jclouds.googlecloudstorage.binders.ComposeObjectBinder;
import org.jclouds.googlecloudstorage.binders.MultipartUploadBinder;
import org.jclouds.googlecloudstorage.binders.UploadBinder; import org.jclouds.googlecloudstorage.binders.UploadBinder;
import org.jclouds.googlecloudstorage.domain.GCSObject; import org.jclouds.googlecloudstorage.domain.GCSObject;
import org.jclouds.googlecloudstorage.domain.ListPage; import org.jclouds.googlecloudstorage.domain.ListPage;
@ -459,4 +460,27 @@ public interface ObjectApi {
@PathParam("destinationObject") String destinationObject, @PathParam("sourceBucket") String sourceBucket, @PathParam("destinationObject") String destinationObject, @PathParam("sourceBucket") String sourceBucket,
@PathParam("sourceObject") String sourceObject, CopyObjectOptions options); @PathParam("sourceObject") String sourceObject, CopyObjectOptions options);
/**
* Stores a new object with metadata.
*
* @see https://developers.google.com/storage/docs/json_api/v1/how-tos/upload#multipart
*
* @param bucketName
* Name of the bucket in which the object to be stored
* @param objectTemplate
* Supply an {@link ObjectTemplate}.
*
* @return a {@link GCSObject}
*/
@Named("Object:multipartUpload")
@POST
@QueryParams(keys = "uploadType", values = "multipart")
@Consumes(MediaType.APPLICATION_JSON)
@Path("/upload/storage/v1/b/{bucket}/o")
@OAuthScopes(STORAGE_FULLCONTROL_SCOPE)
@MapBinder(MultipartUploadBinder.class)
GCSObject multipartUpload(@PathParam("bucket") String bucketName,
@PayloadParam("template") ObjectTemplate objectTemplate, @PayloadParam("payload") Payload payload);
} }

View File

@ -18,6 +18,8 @@ package org.jclouds.googlecloudstorage.features;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNotNull;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import java.io.IOException; import java.io.IOException;
import java.util.Set; import java.util.Set;
@ -55,6 +57,7 @@ import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.beust.jcommander.internal.Sets; import com.beust.jcommander.internal.Sets;
import com.google.common.collect.ImmutableMap;
import com.google.common.hash.HashCode; import com.google.common.hash.HashCode;
import com.google.common.hash.HashFunction; import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing; import com.google.common.hash.Hashing;
@ -66,6 +69,7 @@ public class ObjectApiLiveTest extends BaseGoogleCloudStorageApiLiveTest {
private static final String BUCKET_NAME2 = "jcloudobjectdestination" + UUID.randomUUID(); private static final String BUCKET_NAME2 = "jcloudobjectdestination" + UUID.randomUUID();
private static final String UPLOAD_OBJECT_NAME = "objectOperation.txt"; private static final String UPLOAD_OBJECT_NAME = "objectOperation.txt";
private static final String UPLOAD_OBJECT_NAME2 = "jcloudslogo.jpg"; private static final String UPLOAD_OBJECT_NAME2 = "jcloudslogo.jpg";
private static final String MULTIPART_UPLOAD_OBJECT = "multipart_related.jpg";
private static final String COPIED_OBJECT_NAME = "copyofObjectOperation.txt"; private static final String COPIED_OBJECT_NAME = "copyofObjectOperation.txt";
private static final String COMPOSED_OBJECT = "ComposedObject1.txt"; private static final String COMPOSED_OBJECT = "ComposedObject1.txt";
private static final String COMPOSED_OBJECT2 = "ComposedObject2.json"; private static final String COMPOSED_OBJECT2 = "ComposedObject2.json";
@ -196,7 +200,7 @@ public class ObjectApiLiveTest extends BaseGoogleCloudStorageApiLiveTest {
assertEquals(gcsObject.getName(), COPIED_OBJECT_NAME); assertEquals(gcsObject.getName(), COPIED_OBJECT_NAME);
assertEquals(gcsObject.getContentType(), "text/plain"); assertEquals(gcsObject.getContentType(), "text/plain");
//Test for data // Test for data
PayloadEnclosingImpl impl = api().download(BUCKET_NAME2, COPIED_OBJECT_NAME); PayloadEnclosingImpl impl = api().download(BUCKET_NAME2, COPIED_OBJECT_NAME);
assertNotNull(impl); assertNotNull(impl);
@ -365,12 +369,53 @@ public class ObjectApiLiveTest extends BaseGoogleCloudStorageApiLiveTest {
} }
@Test(groups = "live", dependsOnMethods = "testPatchObjectsWithOptions") @Test(groups = "live", dependsOnMethods = "testPatchObjectsWithOptions")
public void testMultipartJpegUpload() throws IOException {
long contentLength = 32 * 1024L;
ByteSource byteSource = TestUtils.randomByteSource().slice(0, contentLength);
ByteSourcePayload payload = Payloads.newByteSourcePayload(byteSource);
PayloadEnclosingImpl payloadImpl = new PayloadEnclosingImpl(payload);
ObjectTemplate template = new ObjectTemplate();
ObjectAccessControls oacl = ObjectAccessControls.builder().bucket(BUCKET_NAME).entity("allUsers")
.role(ObjectRole.OWNER).build();
// This would trigger server side validation of md5
hcMd5 = byteSource.hash(Hashing.md5());
// This would trigger server side validation of crc32c
hcCrc32c = byteSource.hash(Hashing.crc32c());
template.contentType("image/jpeg").addAcl(oacl).size(contentLength).name(MULTIPART_UPLOAD_OBJECT)
.contentLanguage("en").contentDisposition("attachment").md5Hash(hcMd5)
.customMetadata("custommetakey1", "custommetavalue1").crc32c(hcCrc32c)
.customMetadata(ImmutableMap.of("Adrian", "powderpuff"));
GCSObject gcsObject = api().multipartUpload(BUCKET_NAME, template, payloadImpl.getPayload());
assertThat(gcsObject.getBucket()).isEqualTo(BUCKET_NAME);
assertThat(gcsObject.getName()).isEqualTo(MULTIPART_UPLOAD_OBJECT);
assertThat(gcsObject.getMd5HashCode()).isEqualTo(hcMd5);
assertThat(gcsObject.getCrc32cHashcode()).isEqualTo(hcCrc32c);
assertThat(gcsObject.getAllMetadata()).contains(entry("custommetakey1", "custommetavalue1"),
entry("Adrian", "powderpuff")).doesNotContainKey("adrian");
PayloadEnclosingImpl impl = api().download(BUCKET_NAME, MULTIPART_UPLOAD_OBJECT);
assertThat(ByteStreams2.toByteArrayAndClose(impl.getPayload().openStream())).isEqualTo(
ByteStreams2.toByteArrayAndClose(payloadImpl.getPayload().openStream()));
}
@Test(groups = "live", dependsOnMethods = "testMultipartJpegUpload")
public void testDeleteObject() { public void testDeleteObject() {
api().deleteObject(BUCKET_NAME2, UPLOAD_OBJECT_NAME); api().deleteObject(BUCKET_NAME2, UPLOAD_OBJECT_NAME);
api().deleteObject(BUCKET_NAME2, COMPOSED_OBJECT2); api().deleteObject(BUCKET_NAME2, COMPOSED_OBJECT2);
api().deleteObject(BUCKET_NAME2, COMPOSED_OBJECT); api().deleteObject(BUCKET_NAME2, COMPOSED_OBJECT);
api().deleteObject(BUCKET_NAME2, COPIED_OBJECT_NAME); api().deleteObject(BUCKET_NAME2, COPIED_OBJECT_NAME);
api().deleteObject(BUCKET_NAME, UPLOAD_OBJECT_NAME);
api().deleteObject(BUCKET_NAME, UPLOAD_OBJECT_NAME2); api().deleteObject(BUCKET_NAME, UPLOAD_OBJECT_NAME2);
api().deleteObject(BUCKET_NAME, MULTIPART_UPLOAD_OBJECT);
} }
@Test(groups = "live", dependsOnMethods = "testPatchObjectsWithOptions") @Test(groups = "live", dependsOnMethods = "testPatchObjectsWithOptions")