38.2 Working with S3 (tutorial) - using aws-java-sdk (#38)
This commit is contained in:
24
README.md
24
README.md
@ -486,3 +486,27 @@ systemctl restart docker
|
|||||||
- `ng build -c production`
|
- `ng build -c production`
|
||||||
- upload to S3
|
- upload to S3
|
||||||
- visit `http://portal.shyshkin.net`
|
- visit `http://portal.shyshkin.net`
|
||||||
|
|
||||||
|
#### 38 Save Profile Images to S3
|
||||||
|
|
||||||
|
##### 38.2 Working with S3 (tutorial)
|
||||||
|
|
||||||
|
1. Follow Tutorial
|
||||||
|
- [How to Upload Files to Amazon S3 in Spring Boot](https://www.section.io/engineering-education/spring-boot-amazon-s3/)
|
||||||
|
2. Create S3 Bucket
|
||||||
|
- `portal-user-profile-images`
|
||||||
|
3. Access and secret keys
|
||||||
|
- My Security Credentials
|
||||||
|
- will redirect to `https://console.aws.amazon.com/iam/home?region=eu-north-1#/security_credentials`
|
||||||
|
- Create Access Key
|
||||||
|
- Access key ID: `AKIA...2GBJ`
|
||||||
|
- Secret access key: `LUS...H+yuAW`
|
||||||
|
4. Adding Amazon SDK dependency
|
||||||
|
- `<dependency>`
|
||||||
|
- ` <groupId>com.amazonaws</groupId>`
|
||||||
|
- ` <artifactId>aws-java-sdk</artifactId>`
|
||||||
|
- ` <version>1.12.75</version>`
|
||||||
|
- `</dependency>`
|
||||||
|
5. Create configuration
|
||||||
|
6. Create ProfileImageService implementation
|
||||||
|
|
||||||
|
|||||||
@ -86,6 +86,12 @@
|
|||||||
<artifactId>spring-boot-starter-validation</artifactId>
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.amazonaws</groupId>
|
||||||
|
<artifactId>aws-java-sdk</artifactId>
|
||||||
|
<version>1.12.75</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
@ -108,7 +114,6 @@
|
|||||||
<groupId>org.apache.httpcomponents</groupId>
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
<artifactId>httpclient</artifactId>
|
<artifactId>httpclient</artifactId>
|
||||||
<version>4.5.13</version>
|
<version>4.5.13</version>
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.config;
|
||||||
|
|
||||||
|
import com.amazonaws.auth.AWSCredentials;
|
||||||
|
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||||
|
import com.amazonaws.auth.BasicAWSCredentials;
|
||||||
|
import com.amazonaws.services.s3.AmazonS3;
|
||||||
|
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Profile("image-s3")
|
||||||
|
public class AmazonConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AmazonS3 s3(@Value("${app.amazon-s3.access-key}") String accessKey,
|
||||||
|
@Value("${app.amazon-s3.secret-key}") String secretKey,
|
||||||
|
@Value("${app.amazon-s3.region}") String region) {
|
||||||
|
|
||||||
|
AWSCredentials awsCredentials =
|
||||||
|
new BasicAWSCredentials(accessKey, secretKey);
|
||||||
|
return AmazonS3ClientBuilder
|
||||||
|
.standard()
|
||||||
|
.withRegion(region)
|
||||||
|
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
package net.shyshkin.study.fullstack.supportportal.backend.service;
|
||||||
|
|
||||||
|
import com.amazonaws.AmazonServiceException;
|
||||||
|
import com.amazonaws.SdkClientException;
|
||||||
|
import com.amazonaws.services.s3.AmazonS3;
|
||||||
|
import com.amazonaws.services.s3.model.ObjectListing;
|
||||||
|
import com.amazonaws.services.s3.model.ObjectMetadata;
|
||||||
|
import com.amazonaws.services.s3.model.S3Object;
|
||||||
|
import com.amazonaws.services.s3.model.S3ObjectInputStream;
|
||||||
|
import com.amazonaws.util.IOUtils;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.shyshkin.study.fullstack.supportportal.backend.exception.domain.ImageStorageException;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@Profile("image-s3")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class S3ProfileImageService implements ProfileImageService {
|
||||||
|
|
||||||
|
private final AmazonS3 amazonS3;
|
||||||
|
|
||||||
|
@Value("${app.amazon-s3.bucket-name}")
|
||||||
|
private String bucketName;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] retrieveProfileImage(UUID userId, String filename) {
|
||||||
|
|
||||||
|
String fileKey = createFileKey(userId, filename);
|
||||||
|
|
||||||
|
try {
|
||||||
|
S3Object object = amazonS3.getObject(bucketName, fileKey);
|
||||||
|
S3ObjectInputStream objectContent = object.getObjectContent();
|
||||||
|
return IOUtils.toByteArray(objectContent);
|
||||||
|
} catch (AmazonServiceException | IOException exception) {
|
||||||
|
throw new ImageStorageException("Failed to download the file from Amazon S3", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String persistProfileImage(UUID userId, MultipartFile profileImage, String filename) {
|
||||||
|
|
||||||
|
// String fileName = String.format("%s", profileImage.getOriginalFilename());
|
||||||
|
String fileKey = createFileKey(userId, filename);
|
||||||
|
|
||||||
|
ObjectMetadata objectMetadata = new ObjectMetadata();
|
||||||
|
objectMetadata.addUserMetadata("Content-Type", profileImage.getContentType());
|
||||||
|
objectMetadata.addUserMetadata("Content-Length", String.valueOf(profileImage.getSize()));
|
||||||
|
|
||||||
|
try {
|
||||||
|
amazonS3.putObject(bucketName, fileKey, profileImage.getInputStream(), objectMetadata);
|
||||||
|
} catch (IOException | SdkClientException exception) {
|
||||||
|
throw new ImageStorageException("Failed to persist to Amazon S3", exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearUserStorage(UUID userId) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
String prefix = userId + "/";
|
||||||
|
ObjectListing objectListing = amazonS3.listObjects(bucketName, prefix);
|
||||||
|
|
||||||
|
objectListing.getObjectSummaries()
|
||||||
|
.forEach(file -> amazonS3.deleteObject(bucketName, file.getKey()));
|
||||||
|
} catch (SdkClientException exception) {
|
||||||
|
throw new ImageStorageException("Failed to delete objects from Amazon S3", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createFileKey(UUID userId, String filename) {
|
||||||
|
return String.format("%s/%s", userId, filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -128,4 +128,17 @@ server.ssl:
|
|||||||
key-store-password: secret # Keystore password
|
key-store-password: secret # Keystore password
|
||||||
key-store-type: PKCS12 # Keystore format
|
key-store-type: PKCS12 # Keystore format
|
||||||
|
|
||||||
|
---
|
||||||
|
spring:
|
||||||
|
config:
|
||||||
|
activate:
|
||||||
|
on-profile: image-s3
|
||||||
|
app:
|
||||||
|
amazon-s3:
|
||||||
|
access-key: ${AMAZON_S3_ACCESS_KEY}
|
||||||
|
secret-key: ${AMAZON_S3_SECRET_KEY}
|
||||||
|
region: eu-north-1
|
||||||
|
bucket-name: portal-user-profile-images
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user