Storing files to S3

To store files to S3, the best way is to use S3 SDK. To use the API of the SDK, we will need to create an S3 client that can be reused between multiple requests.

The following is how we initialize the S3FileStorage, our FileStorage implementation based on S3:

...
@Component("s3FileStorage")
public class S3FileStorage extends AbstractBaseFileStorage {

private Environment environment;
private String rootTempPath;
private AmazonS3 s3;

public S3FileStorage(Environment environment,
@Value("${app.file-storage.temp-folder}") String
rootTempPath) {
this.environment = environment;
this.rootTempPath = rootTempPath;
if ("s3FileStorage".equals(environment.getProperty("app.file-
storage.active"))) {
this.s3 = initS3Client();
}
}
...
}

As you can see, in its constructor, we use a guard to initialize the S3 client only when the active file storage is S3.

This is how initS3Client() looks:

private AmazonS3 initS3Client() {
String s3Region = environment.getProperty("app.file-storage.s3-
region");
Assert.hasText(s3Region, "Property `app.file-storage.s3-region` must
not be blank");

if (environment.acceptsProfiles("dev")) {
log.debug("Initializing dev S3 client with access key and secret
key");

String s3AccessKey = environment.getProperty("app.file-storage.s3-
access-key");
String s3SecretKey = environment.getProperty("app.file-storage.s3-
secret-key");

Assert.hasText(s3AccessKey, "Property `app.file-storage.s3-access-
key` must not be blank");
Assert.hasText(s3SecretKey, "Property `app.file-storage.s3-secret-
key` must not be blank");

BasicAWSCredentials awsCredentials = new
BasicAWSCredentials(s3AccessKey, s3SecretKey);
AWSStaticCredentialsProvider credentialsProvider = new
AWSStaticCredentialsProvider(awsCredentials);

AmazonS3ClientBuilder builder = AmazonS3ClientBuilder.standard();
builder.setRegion(s3Region);
builder.withCredentials(credentialsProvider);
return builder.build();
} else {
log.debug("Initializing default S3 client using IAM role");
return AmazonS3ClientBuilder.standard()
.withCredentials(new InstanceProfileCredentialsProvider(false))
.withRegion(s3Region)
.build();
}
}

As you can see, we use AmazonS3ClientBuilder to create the client instance. We provide two pieces of information to the builder. One is the region, which is configured in our application.properties. The other is the credentials. Since our application will be pushed to Amazon's EC2, we have two options with the credentials. One is to use the access key + secret key to create BasicAWSCredentials. The other is to use the IAM role defined in the EC2 instance to provide the credential information, and in our case, when we're in dev environment, which will be on our laptop, we use the access key + secret key. When we're running on the server, we will use the IAM role.

The following is S3FileStorage implementation of the saveAsTempFile() method:

public TempFile saveAsTempFile(String folder, MultipartFile multipartFile) {
return saveMultipartFileToLocalTempFolder(rootTempPath, folder, multipartFile);
}

As you can see, we simply call the saveMultipartFileToLocalTempFolder() method of AbstractBaseFileStorage.

Here is the implementation of the saveTempFile() method:

public void saveTempFile(TempFile tempFile) {
Assert.notNull(s3, "S3FileStorage must be initialized properly");

String fileKey = tempFile.getFileRelativePath();
String bucketName = environment.getProperty("app.file-storage.s3-
bucket-name");
Assert.hasText(bucketName, "Property `app.file-storage.s3-bucket-
name` must not be blank");

try {
log.debug("Saving file `{}` to s3", tempFile.getFile().getName());
PutObjectRequest putRequest = new PutObjectRequest(bucketName,
fileKey, tempFile.getFile());

putRequest.withCannedAcl(CannedAccessControlList.PublicRead);
s3.putObject(putRequest);
log.debug("File `{}` saved to s3", tempFile.getFile().getName(),
fileKey);
} catch (Exception e) {
log.error("Failed to save file to s3", e);
throw new FileStorageException("Failed to save file `" +
tempFile.getFile().getName() + "` to s3", e);
}
}

In this method, we use the file's relative path as the bucket file key and get the bucket name from application.properties, and to upload a file to S3 with the S3 client is quite straightforward, as you can see. We create PutObjectRequest and make the file accessible publicly. After that, we call the client's putObject() method to save the file to S3.

Here is the implementation of the saveUploaded() method:

public String saveUploaded(String folder, MultipartFile multipartFile) {
Assert.notNull(s3, "S3FileStorage must be initialized properly");

String originalFileName = multipartFile.getOriginalFilename();
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(multipartFile.getSize());
metadata.setContentType(multipartFile.getContentType());
metadata.addUserMetadata("Original-File-Name", originalFileName);
String finalFileName = generateFileName(multipartFile);
String s3ObjectKey = folder + "/" + finalFileName;

String bucketName = environment.getProperty("app.file-storage.s3-
bucket-name");
Assert.hasText(bucketName, "Property `app.file-storage.s3-bucket-
name` must not be blank");

try {
log.debug("Saving file `{}` to s3", originalFileName);
PutObjectRequest putRequest = new PutObjectRequest(
bucketName, s3ObjectKey, multipartFile.getInputStream(),
metadata);

putRequest.withCannedAcl(CannedAccessControlList.PublicRead);
s3.putObject(putRequest);
log.debug("File `{}` saved to s3 as `{}`", originalFileName,
s3ObjectKey);
} catch (Exception e) {
log.error("Failed to save file to s3", e);
throw new FileStorageException("Failed to save file `" +
multipartFile.getOriginalFilename() + "` to s3", e);
}

return s3ObjectKey;
}

In this method, we save the uploaded file, the MultipartFile instance, to S3 directly. It is almost the same as the saveTempFile() method; the only difference is that we create an ObjectMetadata instance to add custom metadata, Original-File-Name, which will show up as a header, x-amz-meta-original-file-name, in the response of the request for getting this file.

The following is the commit record of implementing the card frontend and backend:

Figure 13.6: Implementing the card frontend and backend commit
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.77.153