KotlinのInputStreamとuse関数を使用して、S3からオブジェクトをダウンロードしながら暗号化または復号を行う
ストリーム処理のメリット
メモリ効率
オブジェクトの取得と暗号化を並行して行うことで、全体をメモリにロードすることなく処理することができるため、メモリ使用量が抑えられる。
パフォーマンス
取得と暗号化を並行して行うことで、I/O待ちの時間を短縮させることが期待できる。
エラー処理
use関数は、処理の完了時やエラー発生時に自動的にリソースを解放するため、例外が発生した場合でも適切なリソースの解放が行われ、リソースリークを防止できる。
実装
- S3クライアントは、
S3Client
を使用する-
S3Client
は「AWS SDK for Java 2.x」で使用可能
-
- 暗号化と復号には、
AWS Encryption SDK
を使用する
val s3Client = S3Client.builder()
.region(Region.AP_NORTHEAST_1)
.build()
// AWS Encryption SDKのエントリポイント
val awsCrypto = AwsCrypto.builder().build()
val masterKeyProvider = KmsMasterKeyProvider.builder().buildStrict("ここにKMSのARN")
// オブジェクトをダウンロードしながら暗号化または復号する
s3Client.getObject {
it.bucket("ここにダウンロード先のバケット名")
it.key("ここにダウンロード先のオブジェクトキー")
}.use { inputStream ->
// 暗号化
val byteArray = awsCrypto.createEncryptingStream(masterKeyProvider, inputStream).readAllBytes()
// 復号の場合、createDecryptingStreamを使用する
// val byteArray = awsCrypto.createDecryptingStream(masterKeyProvider, inputStream).readAllBytes()
s3Client.putObject(
PutObjectRequest.builder()
.bucket("ここにアップロード先のバケット名")
.key("ここにアップロード先のオブジェクトキー")
.build(),
RequestBody.fromBytes(byteArray)
)
}
要件次第ですが、実際には以下のようなことに注意すること!!
- 暗号化したデータを保存したら、生のデータは削除する
- サイズが大きい場合、マルチパートアップロードを使用する
SdkClientExceptionが発生した場合
S3クライアントを作成する際にSdkClientException
が発生した場合、
以下のように、明示的に同期HTTPクライアントを指定すると正常に動作するかと思われる。
// ここでSdkClientExceptionが発生した場合
val s3Client = S3Client.builder()
.region(Region.AP_NORTHEAST_1)
.build()
// 明示的に同期HTTPクライアントを指定する
val httpClient = ApacheHttpClient.builder().build()
val s3Client = S3Client.builder()
.region(Region.AP_NORTHEAST_1)
.httpClient(httpClient)
.build()
デフォルトでApacheHttpClient
が設定されている(参考)
Gradleの設定
dependencies {
implementation("com.amazonaws:aws-java-sdk-s3:ここにバージョン")
implementation("com.amazonaws:aws-encryption-sdk-java:ここにバージョン")
// ApacheHttpClientを使用する際に必要
implementation("software.amazon.awssdk:apache-client:ここにバージョン")
}
それぞれのバージョンは、こちらから確認すること。
Mavenを使用している場合は、こちらを参考に設定してください。