S3に画像をアップロードする
S3へのアップロード方法についてはこのREADMEを見た
seratch/AWScala: Using AWS SDK on the Scala REPL
Actor に新しく S3 のクラスを Inject しようとしてエラー
code:sh
akka.actor.ActorInitializationException: akka://application/user/image-actor: exception during creation
at akka.actor.ActorInitializationException$.apply(Actor.scala:195)
at akka.actor.ActorCell.create(ActorCell.scala:657)
at akka.actor.ActorCell.invokeAll$1(ActorCell.scala:509)
at akka.actor.ActorCell.systemInvoke(ActorCell.scala:531)
at akka.dispatch.Mailbox.processAllSystemMessages(Mailbox.scala:294)
at akka.dispatch.Mailbox.run(Mailbox.scala:229)
at akka.dispatch.Mailbox.exec(Mailbox.scala:242)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: com.google.inject.ProvisionException: Unable to provision, see the following errors:
1) Error injecting constructor, java.util.NoSuchElementException: None.get
at storage.ImageS3.<init>(ImageS3.scala:16)
while locating storage.ImageS3
while locating image.ImageStorage
for the 4th parameter of actor.ImageActor.<init>(ImageActor.scala:19)
while locating actor.ImageActor
普通に creadential の設定がミスっているだけだった
IAM Role の書き方
code:json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::lgtmoon-dev",
"arn:aws:s3:::lgtmoon-dev/*"
]
}
]
}
lgtmoon-dev と lgtmoon-dev/* の両方が必要らしい
AWScala で put すると Access Denied になる
最初こういうのをかいていた
code:scala
import java.io.File
import awscala.s3.{Bucket, S3}
import awscala.Region
val accessKeyId: String = "XXXXX"
val secretAccessKey: String = "XXXXX"
val bucketName: String = "バケット名"
implicit val s3: S3 = S3(accessKeyId, secretAccessKey)(Region.AP_NORTHEAST_1)
val bucket: Bucket = s3.bucket(bucketName).get
val file = new File("ファイルパス")
bucket.put(convertedImage.id.toString, file)
これだとエラーになる。
code:sh
error - akka.actor.OneForOneStrategy - Access Denied (Service: Amazon S3; Status Code: 403; Error Code: AccessDenied; Request ID: 7677BAEA234CA9FD; S3 Extended Request ID: 0c/rSFdkRJP7/VErCWP2YivCREyKFUoNJxG2veHDb6rX4wgfIqUngm1KxqhkDhDGcgkSyK3s0o8=) com.amazonaws.services.s3.model.AmazonS3Exception: Access Denied (Service: Amazon S3; Status Code: 403; Error Code: AccessDenied; Request ID: 7677BAEA234CA9FD; S3 Extended Request ID: xxxxx)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.handleErrorResponse(AmazonHttpClient.java:1712)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeOneRequest(AmazonHttpClient.java:1367)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeHelper(AmazonHttpClient.java:1113)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.doExecute(AmazonHttpClient.java:770)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeWithTimer(AmazonHttpClient.java:744)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.execute(AmazonHttpClient.java:726)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.access$500(AmazonHttpClient.java:686)
at com.amazonaws.http.AmazonHttpClient$RequestExecutionBuilderImpl.execute(AmazonHttpClient.java:668)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:532)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:512)
at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:5052)
at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:4998)
at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:4992)
at com.amazonaws.services.s3.AmazonS3Client.listBuckets(AmazonS3Client.java:993)
at com.amazonaws.services.s3.AmazonS3Client.listBuckets(AmazonS3Client.java:999)
at awscala.s3.S3.buckets(S3.scala:62)
at awscala.s3.S3.buckets$(S3.scala:62)
at awscala.s3.S3Client.buckets(S3.scala:337)
at awscala.s3.S3.bucket(S3.scala:64)
at awscala.s3.S3.bucket$(S3.scala:64)
at awscala.s3.S3Client.bucket(S3.scala:337)
at storage.ImageS3.save(ImageS3.scala:48)
at actor.ImageActor$$anonfun$receive$1.applyOrElse(ImageActor.scala:30)
at akka.actor.Actor.aroundReceive(Actor.scala:534)
at akka.actor.Actor.aroundReceive$(Actor.scala:532)
at actor.ImageActor.aroundReceive(ImageActor.scala:18)
at akka.actor.ActorCell.receiveMessage(ActorCell.scala:573)
at akka.actor.ActorCell.invoke(ActorCell.scala:543)
at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:269)
at akka.dispatch.Mailbox.run(Mailbox.scala:230)
at akka.dispatch.Mailbox.exec(Mailbox.scala:242)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
s3.bucket のところでエラーになる
↑の権限だと全てのバケットをリストする権限は与えられていないので、Access Denied になる
code:sh
$ aws s3 ls --profile lgtmoon_dev
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied
s3.bucket の中身を見ると、バケット一覧を取ってきて、その中から該当するバケットを取ってきている
バケット一覧を取るところで権限が無いのでここで死ぬ
ということでこうした
code:scala
implicit val s3: S3 = S3(accessKeyId, secretAccessKey)(Region.AP_NORTHEAST_1)
val bucket = new Bucket(bucketName)
val file = new File(convertedImage.path)
s3.put(bucket, convertedImage.id.toString, file)
これでうごいた
権限の確認
参考
AWS CLIでS3を操作するコマンド一覧 - Qiita
.aws/credentials に書く
code:sh
aws_access_key_id =
aws_secret_access_key =
region = ap-northeast-1
試す
code:sh
$ aws s3 ls s3://lgtmoon-dev --profile lgtmoon_dev
2020-12-24 13:05:42 131222 10
$ aws s3 cp ~/Downloads/IMG_0755.JPG s3://lgtmoon-dev/test --profile lgtmoon_dev
upload: Downloads/IMG_0755.JPG to s3://lgtmoon-dev/test
画像移行シェルスクリプト
curl してきて s3 コマンドでアップロードすることにした
aws の credentials をあらかじめ設定しておく
code:~/.aws/credentials
aws_access_key_id =
aws_secret_access_key =
region = ap-northeast-1
S3のリポジトリをパブリックにする
terraform で行う
acl を private から public-read にしただけ
code:terraform
# S3バケットを作成
resource "aws_s3_bucket" "lgtmoon_bucket" {
bucket = var.lgtmoon_aws_s3_bucket_name
acl = "public-read"
}
かと思ったけど、これだとファイル一覧は見られるが、ファイルの中身は見られない
AWScala 微妙すぎる
ファイルアップロードに必要な権限以外も求められることがある(内部処理がだいぶ謎)
アトラシアンの aws-scala のほうが良いのではないか
ScalaでAWS(S3)にアクセスするライブラリ - Qiita
そもそも content-type がファイルから取れていない問題
以下のブログを参考に
JavaでファイルのMIMEタイプを取得する
こうやったけど
code:scala
val file = new File(convertedImage.path)
val contentType = Files.probeContentType(file.toPath)
null になってしまい取れず。
getContentType でやったら一応取れたけど、file.toURL は deprecated らしい
jMimeMagic か Apache Tika を使うのがいいかもしれん
しかも元記事(翻訳前の記事があった)2020年の記事なので、結構新しい情報っぽい
Apache Tinker を使うことにした
aws-scala を使う
前回の記事
AWScala が微妙過ぎたので、 aws-scala を使う
AWS のライブラリについてまとめた記事
ScalaでAWS(S3)にアクセスするライブラリ - Qiita
Atlassianのaws-scalaを試してみた - たけぞう瀕死ブログ
aws-scala, scala 2.13 に対応しておらずインストールできなかった
そんなことある?!
公式の Scala SDK はアーカイブされていた
amazon-archives/aws-scala-sdk: It's like the AWS SDK for Java, but more Scala-y
Scala:aws-java-sdkを使用してs3にファイルをアップロードする - Qiita
IAM ポリシーの権限もっと絞ってもいいかもしれない
ScalaでもAWS S3互換にファイルをアップロードしたい - レンコン畑でつかまえて
↑の記事だと sdk のバージョンが古いので注意
なんか見ている感じ、 aws-java-sdk-s3 だけ入れれば良いのかな
Maven Repository: com.amazonaws » aws-java-sdk-s3
なんかいい感じには動いた。Content-Type も渡せた
あとは try-catch するだけ
AWScala は binary か file の 2択だったのが微妙?
FileInputStremaが実は渡せたのか?