«Есть несколько хороших способов загрузки контента в корзину S3 в мире Java — в этой статье мы рассмотрим, что для этой цели предоставляет библиотека jclouds.

Чтобы использовать jclouds — особенно API, обсуждаемые в этой статье, эту простую зависимость Maven следует добавить в pom проекта:

<dependency>
   <groupId>org.jclouds</groupId>
   <artifactId>jclouds-allblobstore</artifactId>
   <version>1.5.10</version>
</dependency>

1. Загрузка в Amazon S3

Первый шаг, для доступа к любому из этих API необходимо создать BlobStoreContext:

BlobStoreContext context = 
  ContextBuilder.newBuilder("aws-s3").credentials(identity, credentials)
    .buildView(BlobStoreContext.class);

Он представляет собой точку входа в общую службу хранения ключей и значений, такую ​​как Amazon S3, но не ограничиваясь ею.

Для более конкретной реализации только S3 контекст можно создать аналогичным образом:

BlobStoreContext context = 
  ContextBuilder.newBuilder("aws-s3").credentials(identity, credentials)
    .buildView(S3BlobStoreContext.class);

И даже более конкретно:

BlobStoreContext context = 
  ContextBuilder.newBuilder("aws-s3").credentials(identity, credentials)
    .buildView(AWSS3BlobStoreContext.class);

Когда аутентифицированный контекст больше не нужен, его закрытие требуется для освобождения все ресурсы — потоки и соединения — связанные с ним.

2. Четыре API-интерфейса jclouds S3

Библиотека jclouds предоставляет четыре различных API-интерфейса для загрузки контента в корзину S3, от простых, но негибких до сложных и мощных. Все они получены через BlobStoreContext. Начнем с самого простого.

2.1. Загрузка через Map API

Самый простой способ использования jclouds для взаимодействия с корзиной S3 — представление этой корзины в виде карты. API получается из контекста:

InputStreamMap bucket = context.createInputStreamMap("bucketName");

Затем для загрузки простого HTML-файла:

bucket.putString("index1.html", "<html><body>hello world1</body></html>");

API InputStreamMap предоставляет несколько других типов операций PUT — файлы, необработанные байты — оба для одиночных и объемных.

В качестве примера можно использовать простой интеграционный тест:

@Test
public void whenFileIsUploadedToS3WithMapApi_thenNoExceptions() {
   BlobStoreContext context = 
      ContextBuilder.newBuilder("aws-s3").credentials(identity, credentials)
         .buildView(AWSS3BlobStoreContext.class);

   InputStreamMap bucket = context.createInputStreamMap("bucketName");  

   bucket.putString("index1.html", "<html><body>hello world1</body></html>");
   context.close();
}

2.2. Загрузка через BlobMap

Использование простого Map API является простым, но в конечном счете ограниченным — например, нет возможности передать метаданные о загружаемом содержимом. Когда требуется больше гибкости и настройки, этого упрощенного подхода к загрузке данных в S3 через карту уже недостаточно.

Следующим API, который мы рассмотрим, является API карты BLOB-объектов. Он получен из контекста:

BlobMap bucket = context.createBlobMap("bucketName");

API позволяет клиенту получить доступ к более низкоуровневым данным, таким как Content-Length. , Content-Type, Content-Encoding, хэш eTag и другие; для загрузки нового контента в корзину:

Blob blob = bucket.blobBuilder().name("index2.html").
   payload("<html><body>hello world2</body></html>").
      contentType("text/html").calculateMD5().build();

API также позволяет устанавливать различные полезные данные в запросе на создание.

Простой интеграционный тест для загрузки базового HTML-файла в S3 через API Blob Map:

@Test
public void whenFileIsUploadedToS3WithBlobMap_thenNoExceptions() throws IOException {
   BlobStoreContext context = 
      ContextBuilder.newBuilder("aws-s3").credentials(identity, credentials)
         .buildView(AWSS3BlobStoreContext.class);

   BlobMap bucket = context.createBlobMap("bucketName");

   Blob blob = bucket.blobBuilder().name("index2.html").
      payload("<html><body>hello world2</body></html>").
         contentType("text/html").calculateMD5().build();
   bucket.put(blob.getMetadata().getName(), blob);

   context.close();
}

2.3. Загрузка через BlobStore

В предыдущих API не было возможности загружать содержимое с помощью многокомпонентной загрузки — это делало их плохо подходящими для работы с большими файлами. Это ограничение устранено с помощью следующего API, который мы собираемся рассмотреть, — синхронного API BlobStore.

Это получено из контекста:

BlobStore blobStore = context.getBlobStore();

Чтобы использовать поддержку составных частей и загрузить файл в S3:

Blob blob = blobStore.blobBuilder("index3.html").
   payload("<html><body>hello world3</body></html>").contentType("text/html").build();
blobStore.putBlob("bucketName", blob, PutOptions.Builder.multipart());

Построитель полезных данных тот же, что использовался API BlobMap, поэтому здесь доступна та же гибкость в указании метаданных более низкого уровня о большом двоичном объекте. Разница заключается в параметрах PutOptions, поддерживаемых операцией PUT API, а именно в поддержке составных частей.

Предыдущий интеграционный тест теперь включает несколько частей:

@Test
public void whenFileIsUploadedToS3WithBlobStore_thenNoExceptions() {
   BlobStoreContext context = 
      ContextBuilder.newBuilder("aws-s3").credentials(identity, credentials)
         .buildView(AWSS3BlobStoreContext.class);

   BlobStore blobStore = context.getBlobStore();

   Blob blob = blobStore.blobBuilder("index3.html").
      payload("<html><body>hello world3</body></html>").contentType("text/html").build();
   blobStore.putBlob("bucketName", blob, PutOptions.Builder.multipart());
   context.close();
}

2.4. Загрузка через AsyncBlobStore

Хотя предыдущий API BlobStore был синхронным, для BlobStore также существует асинхронный API — AsyncBlobStore. Аналогичным образом API получается из контекста:

AsyncBlobStore blobStore = context.getAsyncBlobStore();

Единственная разница между ними заключается в том, что асинхронный API возвращает ListenableFuture для асинхронной операции PUT:

Blob blob = blobStore.blobBuilder("index4.html").
   .payload("<html><body>hello world4</body></html>").build();
blobStore.putBlob("bucketName", blob)<strong>.get()</strong>;

Интеграционный тест, отображающий эту операцию, похож на синхронный:

@Test
public void whenFileIsUploadedToS3WithBlobStore_thenNoExceptions() {
   BlobStoreContext context = 
      ContextBuilder.newBuilder("aws-s3").credentials(identity, credentials)
         .buildView(AWSS3BlobStoreContext.class);

   BlobStore blobStore = context.getBlobStore();

   Blob blob = blobStore.blobBuilder("index4.html").
      payload("<html><body>hello world4</body></html>").contentType("text/html").build();
   Future<String> putOp = blobStore.putBlob("bucketName", blob, PutOptions.Builder.multipart());
   putOp.get();
   context.close();
}

3. Заключение

В этой статье мы проанализировали четыре API, которые библиотека jclouds предоставляет для загрузки контента в Amazon S3. Эти четыре API являются универсальными и работают также с другими службами хранения данных типа «ключ-значение», например, с хранилищем Microsoft Azure.

В следующей статье мы рассмотрим специфичный для Amazon S3 API, доступный в jclouds — AWSS3Client. Реализуем операцию загрузки большого файла, динамически рассчитаем оптимальное количество частей для любого заданного файла и выполним загрузку всех частей параллельно.