Amazon S3 o Amazon Simple Storage Service (S3) es un servicio de almacenamiento de objetos en la nube ofrecido por Amazon Web Services (AWS). En este post aprenderemos a gestionar ficheros en S3 con una aplicación Spring Boot utilizando la librería Spring Cloud AWS.
Antes de empezar
Requisitos para realizar el ejercicio:
- SDK Java.
- Maven
- Disponer de una cuenta en Amazon Web Services (AWS). Los primeros 12 meses son gratuitos.
- Tu IDE favorito. En mi caso, utilizo IntelliJ
Paso 1: Crea un bucket de Amazon S3
1.- Un bucket es un contenedor para objetos almacenados en Amazon S3. Accede la consola principal de AWS y utiliza el buscador para localizar el servicio S3.
2.- A continuación, pincha en el botón “Crear bucket”:
3.- Selecciona un nombre único para tu bucket y la región de AWS donde quieres desplegarlo:
4.- Para este ejercicio vamos a deshabilitar el checkbox de bloqueo de todo el acceso público:
5.- Para finalizar haz clic en el botón “Crear bucket” situado en la parte inferior de la pantalla:
Paso 2: Crea un usuario con permisos de acceso al bucket
1.- En la consola de AWS localiza el servicio IAM:
2.– Agrega un nuevo usuario:
3.- Asigna un nombre a tu usuario y selecciona el check “clave de acceso: acceso mediante programación”:
4.- Avanza en el proceso hasta la sección de permisos y asigna al usuario la política AmazonS3FullAccess, la cual le permitirá tener acceso completo al servicio Amazon S3:
5.- Avanzamos hasta el final del proceso y pulsamos en el botón “Crear un usuario”:
6.- Una vez creado el usuario, te aparecerá una pantalla donde podrás ver la clave de acceso y el secret asignados. Guarda estas credenciales porque te hará falta más adelante:
Paso 3: Gestiona Amazon S3 con Spring Boot
A continuación, vamos a crear una aplicación Spring Boot que usará Spring Cloud AWS para gestionar el servicio Amazon S3 que hemos creado en el paso 1. Antes de empezar, si quieres, puedes descargar desde mi cuenta de GitHub el código fuente.
1.- Crea una nueva aplicación Spring Boot
Puedes usar spring Initializer. Añade por el momento las dependencias Spring Web y Lombok y genera el proyecto.
Descomprime el zip del proyecto que acabas de generar y ábrelo con tu IDE favorito.
2.- Añadir dependencia Spring Cloud AWS
Localiza el fichero pom.xml del proyecto y añade la dependencia de Spring Cloud AWS:
<dependencies> <dependency> <groupId>io.awspring.cloud</groupId> <artifactId>spring-cloud-starter-aws-messaging</artifactId> </dependency> </dependencies>
Incluye también la siguiente dependencia en la sección dependencyManagement del fichero pom.xml:
<dependencyManagement> <dependencies> <dependency> <groupId>io.awspring.cloud</groupId> <artifactId>spring-cloud-aws-dependencies</artifactId> <version>2.3.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
3.- Estructura del proyecto
A continuación, puedes ver la estructura de carpetas y clases java del proyecto que vamos a crear.
4.- Application.yml
Actualiza en el fichero de configuración de tu proyecto las credenciales del usuario que has creado en el paso 2 así como la región donde se encuentra tu servicio Amazon S3.
cloud: aws: credentials: access-key: AKIADFWERVAATCKFMB7Y secret-key: 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 region: static: us-east-1 stack: auto: false
4.- Configuration
Crea una clase de configuración que se llame StorageConfig, que se encargará de crear un bean de tipo AmazonS3 mediante el constructor AmazonS3ClientBuilder, el cual realizará la autenticación con S3 a partir de las credenciales que hemos definido en el fichero application.yml.
package robertocrespo.net.aws.config; import … @Configuration public class StorageConfig { @Value("${cloud.aws.credentials.access-key}") private String accessKey; @Value("${cloud.aws.credentials.secret-key}") private String accessSecret; @Value("${cloud.aws.region.static}") private String region; @Bean public AmazonS3 s3Client() { AWSCredentials credentials = new BasicAWSCredentials(accessKey, accessSecret); return AmazonS3ClientBuilder.standard() .withCredentials(new AWSStaticCredentialsProvider(credentials)) .withRegion(region).build(); } }
5.- Model
Crea la clase Asset, que utilizaremos para gestionar los objetos de Amazon S3
package robertocrespo.net.aws.model; import lombok.Builder; import lombok.Value; import java.net.URL; @Value @Builder public class Asset { String name; String key; URL url; }
5.- Repository
Lo primero que haremos será definir la interface S3Repository con las operaciones que nos van a permitir gestionar los objetos en Amazon S3.
package robertocrespo.net.aws.repository; import ...; public interface S3Repository { String uploadFile(String bucketName, String fileName, File fileObj); List<Asset> listObjectsInBucket(String bucket); S3ObjectInputStream getObject(String bucketName, String fileName) throws IOException; byte[] downloadFile(String bucketName, String fileName) throws IOException; void moveObject(String bucketName, String fileKey, String destinationFileKey); void deleteObject(String bucketName, String fileKey); }
A continuación vamos a implementar la interface S3Repository. Por simplicidad, sólo muestro el detalle del método uploadFile(). Puedes consultar aquí la implementación completa de la clase S3RepositoryImpl.
package robertocrespo.net.aws.repository; import ... ; @Repository public class S3RepositoryImpl implements S3Repository{ private AmazonS3 s3Client; @Autowired public S3RepositoryImpl(AmazonS3 s3Client) { this.s3Client = s3Client; } public String uploadFile(String bucketName, String fileName, File fileObj) { s3Client.putObject(new PutObjectRequest(bucketName, fileName, fileObj)); fileObj.delete(); return "File uploaded : " + fileName; } }
5.- Service
Al igual que hicimos con el repositorio, definimos primero la interface del servicio:
package robertocrespo.net.aws.service; import robertocrespo.net.aws.model.Asset; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.util.List; public interface AwsService { String uploadFile(String bucketName, String filePath, MultipartFile file); String getS3FileContent(String bucketName, String fileName) throws IOException; List<Asset> getS3Files(String bucketName) throws IOException; byte[] downloadFile(String bucketName, String fileName) throws IOException; void moveObject(String bucketName, String fileKey, String destinationFileKey); void deleteObject (String bucketName, String fileKey); }
Y a continuación su implementación. De nuevo solo muestro la operación uploadFile. Puedes consultar la implementación completa de la clase AwsServiceImpl aquí.
package robertocrespo.net.aws.service; import ...; @Service public class AwsServiceImpl implements AwsService{ private S3Repository s3Repository; @Autowired public AwsServiceImpl(S3Repository s3Repository) { this.s3Repository = s3Repository; } @Override public String uploadFile(String bucketName, String filePath, MultipartFile file) { File fileObj = convertMultiPartFileToFile(file); String fileName = System.currentTimeMillis() + "_" + file.getOriginalFilename(); return s3Repository.uploadFile(bucketName, filePath + fileName, fileObj); } private File convertMultiPartFileToFile(MultipartFile file) { File convertedFile = new File(file.getOriginalFilename()); try (FileOutputStream fos = new FileOutputStream(convertedFile)) { fos.write(file.getBytes()); } catch (IOException e) { log.error("Error converting multipartFile to file", e); } return convertedFile; } //Accede a al repo en github para consultar la implementación completa del servicio }
5.- Controller
Por último, sólo nos queda implementar el controller del servicio, encargado de atender las peticiones REST. De nuevo, por simplificación, sólo muestro la implementación de la operación uploadFile.
package robertocrespo.net.aws.controller; import ...; @RestController @RequestMapping(value = "/s3") public class AwsController { private AwsService awsService; @Autowired public AwsController(AwsService awsService) { this.awsService = awsService; } return new ResponseEntity<>(list, status); } @PostMapping("/uploadFile") public ResponseEntity<String> uploadFile(@RequestParam(value = "bucketName") String bucketName, @RequestParam(value = "filePath") String filePath, @RequestParam(value = "file") MultipartFile file) { return new ResponseEntity<>(awsService.uploadFile(bucketName, filePath, file), HttpStatus.OK); } //Accede a al repo en github para consultar la implementación completa del servicio }
Aquí te dejó la implementación completa de AwsController.
Paso 4: Testing
Es hora de probar la aplicación Spring Boot que acabamos de acabar. Ejecuta la clase Awss3Application y comprueba que se inicia correctamente. Vamos a construir una petición REST usando Postman para probar la subida de un fichero a Amazon S3 invocando a la operación uploadFile de nuestro Controller.
1.- Establecemos como query params de la request el nombre del bucket S3 y la carpeta donde queremos subir el fichero:
2.- Añadir un parámetro de tipo File en el Body de la petición y seleccionamos el fichero que queremos subir a Amazon S3:
3.- Configura la petición REST para que sea de tipo POST. La URL tendrá una estructura similar a la siguiente:
http://localhost:8080/s3/uploadFile?bucketName=mybucket&filePath=myfolder/
http://localhost:8080/s3/uploadFile?bucketName=mybucket&filePath=myfolder/
4.- Ejecuta la petición REST y comprueba que se ha la operación se ha realizado correctamente:
4.- Usando la consola de AWS, accede a tu bucket y comprueba que el fichero ha subido correctamente:
5.- Si quieres probar el resto de las operaciones de la aplicación Spring Boot, aquí te dejo la colección de postman completa
No responses yet