diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/blobstore/AtmosBlobStore.java b/apis/atmos/src/main/java/org/jclouds/atmos/blobstore/AtmosBlobStore.java
index 9ffd845297..35a8197e8e 100644
--- a/apis/atmos/src/main/java/org/jclouds/atmos/blobstore/AtmosBlobStore.java
+++ b/apis/atmos/src/main/java/org/jclouds/atmos/blobstore/AtmosBlobStore.java
@@ -207,6 +207,16 @@ public class AtmosBlobStore extends BaseBlobStore {
return AtmosUtils.putBlob(sync, crypto, blob2Object, container, blob);
}
+ /**
+ * This implementation invokes {@link AtmosClient#createFile}
+ *
+ * Since there is no etag support in atmos, we just return the path.
+ */
+ @Override
+ public String putBlobMultipart(String container, Blob blob) {
+ return putBlob(container, blob);
+ }
+
/**
* This implementation invokes {@link AtmosClient#deletePath}
*/
diff --git a/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3AsyncBlobStore.java b/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3AsyncBlobStore.java
index b647b38d84..89fe0cc54e 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3AsyncBlobStore.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3AsyncBlobStore.java
@@ -83,7 +83,7 @@ public class S3AsyncBlobStore extends BaseAsyncBlobStore {
private final Provider fetchBlobMetadataProvider;
@Inject
- S3AsyncBlobStore(BlobStoreContext context, BlobUtils blobUtils,
+ protected S3AsyncBlobStore(BlobStoreContext context, BlobUtils blobUtils,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, Supplier defaultLocation,
@Memoized Supplier> locations, S3AsyncClient async, S3Client sync,
BucketToResourceMetadata bucket2ResourceMd, ContainerToBucketListOptions container2BucketListOptions,
diff --git a/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3BlobStore.java b/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3BlobStore.java
index a861dd9494..ed2cc10fa8 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3BlobStore.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3BlobStore.java
@@ -74,7 +74,7 @@ public class S3BlobStore extends BaseBlobStore {
private final Provider fetchBlobMetadataProvider;
@Inject
- S3BlobStore(BlobStoreContext context, BlobUtils blobUtils, Supplier defaultLocation,
+ protected S3BlobStore(BlobStoreContext context, BlobUtils blobUtils, Supplier defaultLocation,
@Memoized Supplier> locations, S3Client sync,
BucketToResourceMetadata bucket2ResourceMd, ContainerToBucketListOptions container2BucketListOptions,
BucketToResourceList bucket2ResourceList, ObjectToBlob object2Blob,
@@ -225,6 +225,19 @@ public class S3BlobStore extends BaseBlobStore {
return sync.putObject(container, blob2Object.apply(blob));
}
+ /**
+ * This implementation invokes {@link S3Client#putObject}
+ *
+ * @param container
+ * bucket name
+ * @param blob
+ * object
+ */
+ @Override
+ public String putBlobMultipart(String container, Blob blob) {
+ return sync.putObject(container, blob2Object.apply(blob));
+ }
+
/**
* This implementation invokes {@link S3Client#deleteObject}
*
diff --git a/apis/swift/src/main/java/org/jclouds/openstack/swift/blobstore/SwiftBlobStore.java b/apis/swift/src/main/java/org/jclouds/openstack/swift/blobstore/SwiftBlobStore.java
index 2f26cf5f6a..009b94a6c5 100644
--- a/apis/swift/src/main/java/org/jclouds/openstack/swift/blobstore/SwiftBlobStore.java
+++ b/apis/swift/src/main/java/org/jclouds/openstack/swift/blobstore/SwiftBlobStore.java
@@ -195,6 +195,19 @@ public class SwiftBlobStore extends BaseBlobStore {
return sync.putObject(container, blob2Object.apply(blob));
}
+ /**
+ * This implementation invokes {@link CommonSwiftClient#putObject}
+ *
+ * @param container
+ * container name
+ * @param blob
+ * object
+ */
+ @Override
+ public String putBlobMultipart(String container, Blob blob) {
+ return putBlob(container, blob);
+ }
+
/**
* This implementation invokes {@link CommonSwiftClient#removeObject}
*
diff --git a/blobstore/src/main/java/org/jclouds/blobstore/BlobStore.java b/blobstore/src/main/java/org/jclouds/blobstore/BlobStore.java
index 82bd9961e1..939ff5c400 100644
--- a/blobstore/src/main/java/org/jclouds/blobstore/BlobStore.java
+++ b/blobstore/src/main/java/org/jclouds/blobstore/BlobStore.java
@@ -199,6 +199,22 @@ public interface BlobStore {
* if the container doesn't exist
*/
String putBlob(String container, Blob blob);
+
+ /**
+ * Adds a {@code Blob} representing the data at location {@code container/blob.metadata.name}
+ * using multipart strategies.
+ *
+ * @param container
+ * container to place the blob.
+ * @param blob
+ * fully qualified name relative to the container.
+ * @param options
+ * byte range or condition options
+ * @return etag of the blob you uploaded, possibly null where etags are unsupported
+ * @throws ContainerNotFoundException
+ * if the container doesn't exist
+ */
+ String putBlobMultipart(String container, Blob blob);
/**
* Retrieves the metadata of a {@code Blob} at location {@code container/name}
diff --git a/core/pom.xml b/core/pom.xml
index 7073c21e5b..1bc48d78ea 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -119,6 +119,11 @@
jsr305
1.3.9
+
+ org.jboss.netty
+ netty
+ 3.2.4.Final
+
diff --git a/core/src/main/java/org/jclouds/io/Payloads.java b/core/src/main/java/org/jclouds/io/Payloads.java
index e143183ceb..854fb6126a 100644
--- a/core/src/main/java/org/jclouds/io/Payloads.java
+++ b/core/src/main/java/org/jclouds/io/Payloads.java
@@ -35,6 +35,7 @@ import javax.annotation.Nullable;
import org.jclouds.crypto.CryptoStreams;
import org.jclouds.io.payloads.ByteArrayPayload;
+import org.jclouds.io.payloads.ChunkedFilePayload;
import org.jclouds.io.payloads.FilePayload;
import org.jclouds.io.payloads.InputStreamPayload;
import org.jclouds.io.payloads.StringPayload;
@@ -83,6 +84,10 @@ public class Payloads {
public static FilePayload newFilePayload(File data) {
return new FilePayload(checkNotNull(data, "data"));
}
+
+ public static ChunkedFilePayload newChunkedFilePayload(File data, int part, long chunkOffset, long chunkSize) {
+ return new ChunkedFilePayload(checkNotNull(data, "data"), part, chunkOffset, chunkSize);
+ }
public static UrlEncodedFormPayload newUrlEncodedFormPayload(Multimap formParams, char... skips) {
return new UrlEncodedFormPayload(formParams, skips);
diff --git a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3ContextBuilder.java b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3ContextBuilder.java
index 2a717f8eaf..071663445a 100644
--- a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3ContextBuilder.java
+++ b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/AWSS3ContextBuilder.java
@@ -22,8 +22,10 @@ package org.jclouds.aws.s3;
import java.util.List;
import java.util.Properties;
+import org.jclouds.aws.s3.blobstore.config.AWSS3BlobStoreContextModule;
import org.jclouds.aws.s3.config.AWSS3RestClientModule;
import org.jclouds.s3.S3ContextBuilder;
+import org.jclouds.s3.blobstore.config.S3BlobStoreContextModule;
import com.google.inject.Module;
@@ -37,6 +39,11 @@ public class AWSS3ContextBuilder extends S3ContextBuilder {
super(props);
}
+ @Override
+ protected void addContextModule(List modules) {
+ modules.add(new AWSS3BlobStoreContextModule());
+ }
+
@Override
protected void addClientModule(List modules) {
modules.add(new AWSS3RestClientModule());
diff --git a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3ClientLiveTest.java b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3ClientLiveTest.java
index 5e54c2f966..6833bda5b1 100644
--- a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3ClientLiveTest.java
+++ b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3ClientLiveTest.java
@@ -27,11 +27,15 @@ import static org.jclouds.io.Payloads.newByteArrayPayload;
import static org.testng.Assert.assertEquals;
import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;
+import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.KeyNotFoundException;
+import org.jclouds.blobstore.domain.Blob;
import org.jclouds.http.BaseJettyTest;
import org.jclouds.http.apachehc.config.ApacheHCHttpCommandExecutorServiceModule;
import org.jclouds.io.Payload;
@@ -43,6 +47,7 @@ import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
+import com.google.common.io.ByteStreams;
import com.google.common.io.InputSupplier;
import com.google.inject.Module;
@@ -134,4 +139,23 @@ public class AWSS3ClientLiveTest extends S3ClientLiveTest {
returnContainer(containerName);
}
}
+
+ public void testMultipartChunkedFileStream() throws IOException, InterruptedException {
+
+ FileOutputStream fous = new FileOutputStream(new File("target/const.txt"));
+ ByteStreams.copy(oneHundredOneConstitutions.getInput(), fous);
+ fous.flush();
+ fous.close();
+ String containerName = getContainerName();
+
+ try {
+ BlobStore blobStore = context.getBlobStore();
+ blobStore.createContainerInLocation(null, containerName);
+ Blob blob = blobStore.blobBuilder("const.txt")
+ .payload(new File("target/const.txt")).build();
+ blobStore.putBlobMultipart(containerName, blob);
+ } finally {
+ returnContainer(containerName);
+ }
+ }
}
diff --git a/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/AzureBlobStore.java b/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/AzureBlobStore.java
index 3764648c31..88b0a11f19 100644
--- a/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/AzureBlobStore.java
+++ b/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/AzureBlobStore.java
@@ -192,6 +192,19 @@ public class AzureBlobStore extends BaseBlobStore {
return sync.putBlob(container, blob2AzureBlob.apply(blob));
}
+ /**
+ * This implementation invokes {@link AzureBlobClient#putObject}
+ *
+ * @param container
+ * container name
+ * @param blob
+ * object
+ */
+ @Override
+ public String putBlobMultipart(String container, Blob blob) {
+ return putBlob(container, blob);
+ }
+
/**
* This implementation invokes {@link AzureBlobClient#deleteObject}
*