Utilizzo del client Java JetS3t con Amazon S3

1. Panoramica

In questo tutorial, useremo la libreria JetS3t con Amazon S3.

In poche parole, creeremo bucket, vi scriveremo dati, rileggeremo i dati, li copieremo, quindi li elencheremo ed elimineremo.

2. Installazione JetS3t

2.1. Dipendenza da Maven

Innanzitutto, dobbiamo aggiungere la libreria NATS e Apache HttpClient al nostro pom.xml :

 org.lucee jets3t 0.9.4.0006L   org.apache.httpcomponents httpclient 4.5.5  

Maven Central ha l'ultima versione della libreria JetS3t e l'ultima versione di HttpClient. La fonte per JetS3t può essere trovata qui.

Useremo Apache Commons Codec per uno dei nostri test, quindi lo aggiungeremo anche al nostro pom.xml :

 org.lucee commons-codec 1.10.L001  

Maven Central ha l'ultima versione qui.

2.2. Chiavi Amazon AWS

Avremo bisogno di chiavi di accesso AWS per connetterci al servizio di archiviazione S3. Un account gratuito può essere creato qui.

Dopo aver creato un account, dobbiamo creare un set di chiavi di sicurezza. La documentazione sugli utenti e le chiavi di accesso è disponibile qui.

JetS3t utilizza la registrazione di Apache Commons, quindi lo useremo anche quando vogliamo stampare informazioni su ciò che stiamo facendo.

3. Collegamento a un archivio semplice

Ora che abbiamo una chiave di accesso AWS e una chiave segreta, possiamo connetterci allo storage S3.

3.1. Connessione ad AWS

Innanzitutto, creiamo le credenziali AWS e quindi le usiamo per connettersi al servizio:

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

RestS3Service è la nostra connessione ad Amazon S3. Usa HttpClient per comunicare con S3 su REST.

3.2. Verifica della connessione

Possiamo verificare che ci siamo collegati correttamente al servizio elencando i bucket:

S3Bucket[] myBuckets = s3Service.listAllBuckets(); 

A seconda che abbiamo creato o meno bucket in precedenza, l'array potrebbe essere vuoto, ma se l'operazione non genera un'eccezione, abbiamo una connessione valida.

4. Gestione benne

Con una connessione ad Amazon S3, possiamo creare bucket per conservare i nostri dati. S3 è un sistema di archiviazione di oggetti. I dati vengono caricati come oggetti e archiviati in bucket.

Poiché tutti i bucket S3 condividono lo stesso spazio dei nomi globale, ognuno deve avere un nome univoco.

4.1. Creazione di un secchio

Proviamo a creare un nome per il bucket " mybucket ":

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

Questo fallisce con un'eccezione:

org.jets3t.service.S3ServiceException: Service Error Message. -- ResponseCode: 409, ResponseStatus: Conflict, XML Error Message:  BucketAlreadyExists 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)

Il nome " mybucket " è, prevedibilmente, già preso. Per il resto del tutorial, creeremo i nostri nomi.

Riproviamo con un nome diverso:

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

Con un nome univoco, la chiamata ha esito positivo e vediamo le informazioni sul nostro bucket:

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

4.2. Eliminazione di un bucket

Eliminare un bucket è facile come crearlo, tranne per una cosa; i secchi devono essere vuoti prima di poter essere rimossi!

s3Service.deleteBucket("myuniquename"); 

Questo genererà un'eccezione per un bucket che non è vuoto.

4.3. Specificare la regione del bucket

I bucket possono essere creati in un data center specifico. Per JetS3t l'impostazione predefinita è Northern Virginia negli Stati Uniti o "us-east-1".

Possiamo sovrascriverlo specificando una regione diversa:

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 ha un ampio elenco di regioni definite come costanti.

5. Carica, scarica ed elimina dati

Una volta che abbiamo un secchio, possiamo aggiungervi oggetti. I secchi sono pensati per durare a lungo e non ci sono limiti rigidi alle dimensioni o al numero di oggetti che un secchiello può contenere.

Data is uploaded to S3 by creating S3Objects.We can upload data a from an InputStream,but JetS3t also provides convenience methods for Stringsand Files.

5.1. StringData

Let's take a look at Stringsfirst:

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

Similar to buckets, objects have names, however, object names only live inside their buckets, so we don't have to worry about them being globally unique.

We create the object by passing a name and the data to the constructor. Then we store it with putObject.

When we use this method to store Stringswith JetS3t, it sets the correct content type for us.

Let's query S3 for information about our object and look at the content type:

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

ObjectDetailsOnly()retrieves the objects metadata without downloading it. When we log the content type we see:

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

JetS3t identified the data as text and set the length for us.

Let's download the data and compare it to what we uploaded:

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));

Data is retrieved in the same S3Objectwe use to upload it, with the bytes available in a DataInputStream.

5.2. File Data

The process for uploading files is similar to Strings:

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

When S3Objectsare passed a File they derive their name from the base name of the files they contain:

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

JetS3t takes the File and uploads it for us.It will attempt to load a mime.types filefrom the classpath and use it to identify the type of file and sent content type appropriately.

If we retrieve the object info of our file upload and get the content type we see:

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

Let's download our file to a new one and compare the contents:

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));

Similar to Stringswe downloaded the object and used the DataInputStream to create a new file. Then we calculated an MD5 hash for both files and compared them.

5.3. Streaming Data

When we upload objects other than Stringsor Files,we have a bit more work to do:

ArrayList 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); 

We need to set our content type and length before uploading.

Retrieving this stream means reversing the process:

S3Object newStreamObject = s3Service.getObject(BucketName, "stream"); ObjectInputStream objectInputStream = new ObjectInputStream( newStreamObject.getDataInputStream()); ArrayList newNumbers = (ArrayList) 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)); 

For different data types, the content type property can be used to select a different method for decoding the object.

6. Copying, Moving and Renaming Data

6.1. Copying Objects

Objects can be copied inside S3, without retrieving them.

Let's copy our test file from section 5.2, and verify the result:

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)); 

We can copy objects inside the same bucket, or between two different ones.

If the last argument is true, the copied object will receive new metadata. Otherwise, it will retain the source object's metadata.

If we want to modify the metadata, we can set the flag to true:

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

6.2. Moving Objects

Objects can be moved to another S3 bucket in the same region.A move operation is a copy then a delete operation.

Se l'operazione di copia non riesce, l'oggetto di origine non viene eliminato. Se l'operazione di eliminazione non riesce, l'oggetto continuerà a esistere nell'origine e anche nella posizione di destinazione.

Lo spostamento di un oggetto è simile alla copia:

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

6.3. Ridenominazione di oggetti

JetS3t ha un metodo conveniente per rinominare gli oggetti. Per cambiare il nome di un oggetto lo chiamiamo semplicemente con un nuovo S3Object :

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

7. Conclusione

In questo tutorial, abbiamo utilizzato JetS3t per connetterci ad Amazon S3. Abbiamo creato ed eliminato i bucket. Quindi abbiamo aggiunto diversi tipi di dati ai bucket e recuperato i dati. Per concludere, abbiamo copiato e spostato i nostri dati.

Esempi di codice, come sempre, possono essere trovati su GitHub.