Перейти к основному содержимому

Использование Java-клиента JetS3t с Amazon S3

· 8 мин. чтения

1. Обзор

В этом руководстве мы будем использовать библиотеку JetS3t с Amazon S3.

Проще говоря, мы будем создавать сегменты, записывать в них данные, считывать данные, копировать их, а затем перечислять и удалять их.

2. Установка JetS3t

2.1. Зависимость от Maven

Во-первых, нам нужно добавить библиотеку NATS и Apache HttpClient в наш pom.xml :

<dependency>
<groupId>org.lucee</groupId>
<artifactId>jets3t</artifactId>
<version>0.9.4.0006L</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.5</version>
</dependency>

Maven Central имеет последнюю версию библиотеки JetS3t и последнюю версию HttpClient . Исходники для JetS3t можно найти здесь .

Мы будем использовать кодек Apache Commons для одного из наших тестов, поэтому мы также добавим его в наш pom.xml :

<dependency>
<groupId>org.lucee</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10.L001</version>
</dependency>

Maven Central имеет последнюю версию здесь .

2.2. Ключи Amazon AWS

Нам потребуются ключи доступа AWS для подключения к службе хранилища S3. Здесь можно создать бесплатную учетную запись .

После того, как у нас есть учетная запись, нам нужно создать набор ключей безопасности. Документация о пользователях и ключах доступа доступна здесь.

JetS3t использует ведение журнала Apache Commons, поэтому мы также будем использовать его, когда захотим распечатать информацию о том, что мы делаем.

3. Подключение к простому хранилищу

Теперь, когда у нас есть ключ доступа и секретный ключ AWS, мы можем подключиться к хранилищу S3.

3.1. Подключение к АВС

Сначала мы создаем учетные данные AWS, а затем используем их для подключения к сервису:

AWSCredentials awsCredentials 
= new AWSCredentials("access key", "secret key");
s3Service = new RestS3Service(awsCredentials);

RestS3Service — это наше подключение к Amazon S3. Он использует HttpClient для связи с S3 через REST.

3.2. Проверка соединения

Мы можем убедиться, что мы успешно подключились к сервису, перечислив сегменты:

S3Bucket[] myBuckets = s3Service.listAllBuckets();

В зависимости от того, создавали ли мы сегменты ранее или нет, массив может быть пустым, но если операция не выдает исключение, у нас есть допустимое соединение.

4. Управление сегментами

Подключившись к Amazon S3, мы можем создавать корзины для хранения наших данных. S3 — это система хранения объектов. Данные загружаются в виде объектов и хранятся в сегментах.

Поскольку все сегменты S3 используют одно и то же глобальное пространство имен, каждое из них должно иметь уникальное имя.

4.1. Создание ведра

Попробуем создать ведро с именем « mybucket »:

S3Bucket bucket = s3Service.createBucket("mybucket");

Это не удается с исключением:

org.jets3t.service.S3ServiceException: Service Error Message.
-- ResponseCode: 409, ResponseStatus: Conflict, XML Error Message:
<!--?xml version="1.0" encoding="UTF-8"?-->
<code>BucketAlreadyExists</code> The requested bucket name is not available.
The bucket namespace is shared by all users of the system.
Please select a different name and try again.
mybucket 07BE34FF3113ECCF
at org.jets3t.service.S3Service.createBucket(S3Service.java:1586)

Имя « mybucket », как и ожидалось, уже занято. В оставшейся части урока мы будем придумывать наши имена.

Попробуем еще раз с другим именем:

S3Bucket bucket = s3Service.createBucket("myuniquename");
log.info(bucket);

С уникальным именем вызов проходит успешно, и мы видим информацию о нашем ведре:

[INFO] JetS3tClient - S3Bucket
[name=myuniquename,location=US,creationDate=Sat Mar 31 16:47:47 EDT 2018,owner=null]

4.2. Удаление корзины

Удалить ведро так же просто, как и создать, за исключением одного; ведра должны быть пустыми, прежде чем их можно будет удалить!

s3Service.deleteBucket("myuniquename");

Это вызовет исключение для ведра, которое не пусто.

4.3. Указание области сегмента

Сегменты можно создавать в конкретном центре обработки данных. Для JetS3t по умолчанию используется Северная Вирджиния в США или «us-east-1».

Мы можем переопределить это, указав другой регион:

S3Bucket euBucket 
= s3Service.createBucket("eu-bucket", S3Bucket.LOCATION_EUROPE);
S3Bucket usWestBucket = s3Service
.createBucket("us-west-bucket", S3Bucket.LOCATION_US_WEST);
S3Bucket asiaPacificBucket = s3Service
.createBucket("asia-pacific-bucket", S3Bucket.LOCATION_ASIA_PACIFIC);

JetS3t имеет обширный список регионов, определенных как константы.

5. Загружать, скачивать и удалять данные

Когда у нас есть ведро, мы можем добавлять в него объекты. Ведра предназначены для длительного использования, и нет жестких ограничений на размер или количество объектов, которые может содержать ведро.

Данные загружаются в S3 путем создания S3Objects. Мы можем загружать данные из InputStream, но JetS3t также предоставляет удобные методы для строк и файлов .

5.1. Строковые данные

Давайте сначала посмотрим на строки :

S3Object stringObject = new S3Object("object name", "string object");
s3Service.putObject("myuniquebucket", stringObject);

Как и у корзин, у объектов есть имена, однако имена объектов живут только внутри своих корзин, поэтому нам не нужно беспокоиться о том, что они глобально уникальны.

Мы создаем объект, передавая имя и данные конструктору. Затем мы сохраняем его с помощью putObject.

Когда мы используем этот метод для хранения строк с помощью JetS3t, он устанавливает для нас правильный тип содержимого.

Давайте запросим у S3 информацию о нашем объекте и посмотрим на тип контента:

StorageObject objectDetailsOnly 
= s3Service.getObjectDetails("myuniquebucket", "my string");
log.info("Content type: " + objectDetailsOnly.getContentType() + " length: "
+ objectDetailsOnly.getContentLength());

ObjectDetailsOnly() извлекает метаданные объектов, не загружая их. Когда мы регистрируем тип контента, мы видим:

[INFO] JetS3tClient - Content type: text/plain; charset=utf-8 length: 9

JetS3t идентифицировал данные как текст и задал нам длину.

Давайте загрузим данные и сравним их с тем, что мы загрузили:

S3Object downloadObject = 
s3Service.getObject("myuniquebucket, "string object");
String downloadString = new BufferedReader(new InputStreamReader(
object.getDataInputStream())).lines().collect(Collectors.joining("\n"));

assertTrue("string object".equals(downloadString));

Данные извлекаются из того же S3Object , который мы используем для их загрузки, а байты доступны в DataInputStream.

5.2. Данные файла

Процесс загрузки файлов аналогичен Strings :

File file = new File("src/test/resources/test.jpg");
S3Object fileObject = new S3Object(file);
s3Service.putObject("myuniquebucket", fileObject);

Когда S3Objects передается файл , они получают свое имя от базового имени файлов, которые они содержат:

[INFO] JetS3tClient - File object name is test.jpg

JetS3t берет файл и загружает его для нас. Он попытается загрузить файл mime.types из пути к классам и использовать его для правильной идентификации типа файла и типа отправленного контента.

Если мы получим информацию об объекте нашей загрузки файла и получим тип контента, мы увидим:

[INFO] JetS3tClient - Content type:application/octet-stream

Скачаем наш файл на новый и сравним содержимое:

String getFileMD5(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(new File(filename))) {
return DigestUtils.md5Hex(fis);
}
}

S3Object fileObject = s3Service.getObject("myuniquebucket", "test.jpg");
File newFile = new File("/tmp/newtest.jpg");
Files.copy(fileObject.getDataInputStream(), newFile.toPath(),
StandardCopyOption.REPLACE_EXISTING);
String origMD5 = getFileMD5("src/test/resources/test.jpg");
String newMD5 = getFileMD5("src/test/resources/newtest.jpg");
assertTrue(origMD5.equals(newMD5));

Как и в случае со строками , мы загрузили объект и использовали DataInputStream для создания нового файла. Затем мы вычислили хэш MD5 для обоих файлов и сравнили их.

5.3. Потоковые данные

Когда мы загружаем объекты, отличные от строк или файлов, у нас есть немного больше работы:

ArrayList<Integer> numbers = new ArrayList<>();
// adding elements to the ArrayList

ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(bytes);
objectOutputStream.writeObject(numbers);

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes.toByteArray());

S3Object streamObject = new S3Object("stream");
streamObject.setDataInputStream(byteArrayInputStream);
streamObject.setContentLength(byteArrayInputStream.available());
streamObject.setContentType("binary/octet-stream");

s3Service.putObject(BucketName, streamObject);

Нам нужно установить тип и длину нашего контента перед загрузкой.

Получение этого потока означает обратный процесс:

S3Object newStreamObject = s3Service.getObject(BucketName, "stream");

ObjectInputStream objectInputStream = new ObjectInputStream(
newStreamObject.getDataInputStream());
ArrayList<Integer> newNumbers = (ArrayList<Integer>) objectInputStream
.readObject();

assertEquals(2, (int) newNumbers.get(0));
assertEquals(3, (int) newNumbers.get(1));
assertEquals(5, (int) newNumbers.get(2));
assertEquals(7, (int) newNumbers.get(3));

Для разных типов данных свойство типа содержимого можно использовать для выбора другого метода декодирования объекта.

6. Копирование, перемещение и переименование данных

6.1. Копирование объектов

Объекты можно копировать внутри S3, не извлекая их.

Скопируем наш тестовый файл из раздела 5.2 и проверим результат:

S3Object targetObject = new S3Object("testcopy.jpg");
s3Service.copyObject(
BucketName, "test.jpg",
"myuniquebucket", targetObject, false);
S3Object newFileObject = s3Service.getObject(
"myuniquebucket", "testcopy.jpg");

File newFile = new File("src/test/resources/testcopy.jpg");
Files.copy(
newFileObject.getDataInputStream(),
newFile.toPath(),
REPLACE_EXISTING);
String origMD5 = getFileMD5("src/test/resources/test.jpg");
String newMD5 = getFileMD5("src/test/resources/testcopy.jpg");

assertTrue(origMD5.equals(newMD5));

Мы можем копировать объекты внутри одного ведра или между двумя разными.

Если последний аргумент верен, скопированный объект получит новые метаданные. В противном случае он сохранит метаданные исходного объекта.

Если мы хотим изменить метаданные, мы можем установить флаг в значение true:

targetObject = new S3Object("testcopy.jpg");
targetObject.addMetadata("My_Custom_Field", "Hello, World!");
s3Service.copyObject(
"myuniquebucket", "test.jpg",
"myuniquebucket", targetObject, true);

6.2. Движущиеся объекты

Объекты можно перемещать в другую корзину S3 в том же регионе. Операция перемещения — это копирование, а затем операция удаления.

В случае сбоя операции копирования исходный объект не удаляется. Если операция удаления не удалась, объект все еще будет существовать в источнике, а также в целевом местоположении.

Перемещение объекта похоже на его копирование:

s3Service.moveObject(
"myuniquebucket",
"test.jpg",
"myotheruniquebucket",
new S3Object("spidey.jpg"),
false);

6.3. Переименование объектов

JetS3t имеет удобный метод переименования объектов. Чтобы изменить имя объекта, мы просто вызываем его с новым S3Object :

s3Service.renameObject(
"myuniquebucket", "test.jpg", new S3Object("spidey.jpg"));

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

В этом руководстве мы использовали JetS3t для подключения к Amazon S3. Мы создавали и удаляли ведра. Затем мы добавили разные типы данных в ведра и извлекли данные. Чтобы подвести итог, мы скопировали и переместили наши данные.

Образцы кода, как всегда, можно найти на GitHub .