AWScala
Scala から AWS S3 にファイルをアップロードするのに、 AWScala というライブラリを利用していました。
S3 のライブラリを利用するには、 build.sbt の libraryDependencies
に以下を追加します。
"com.github.seratch" %% "awscala-s3" % "0.8.4",
しかし、若干ハマりどころがあったのでまとめておきます。
AWS IAM の設定
まず、AWS IAM で、特定のバケットのみのアクセスを許可したユーザーを作成します。権限設定は以下の通り。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:*", "Resource": [ "arn:aws:s3:::バケット名", "arn:aws:s3:::バケット名/*" ] } ] }
これについては以下の記事にもまとめたとおりです。
Scala のコード
上記 IAM の権限をつけたユーザーのアクセスキーを発行し、以下のようにコードを書きました。
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)
しかし、このコードはエラーになります。
com.amazonaws.services.s3.model.AmazonS3Exception: Access Denied ( Service: Amazon S3; Status Code: 403; Error Code: AccessDenied; ...
試しに AWS CLI をつかって S3 にアクセスするとエラーになりません。これでエラーになる場合は権限が正しく付与されていないのですが、アップロードは成功しているので、場決tの権限は正しく付与されています。
$ aws s3 cp ~/Downloads/IMG_0755.JPG s3://バケット名/ファイル名 --profile lgtmoon_dev upload: Downloads/IMG_0755.JPG to s3://lgtmoon-dev/test
エラーにならない方法
implicit val s3: S3 = S3(accessKeyId, secretAccessKey)(Region.AP_NORTHEAST_1) val bucket = new Bucket(bucketName) val file = new File("ファイルパス") s3.put(bucket, convertedImage.id.toString, file)
val bucket: Bucket = s3.bucket(bucketName).get
を使うとエラーになるので、
val bucket = new Bucket(bucketName)
とし、 s3.put
メソッドに bucket を渡す形にしたらエラーがでなくなりました。
なぜエラーになるのか
s3.bucket(bucketName)
は、内部では S3 の API でバケット一覧を取得し、その中から指定したバケット名のバケットを取得する、という処理になっています。
def bucket(name: String): Option[Bucket] = buckets.find(_.name == name)
しかし、アカウントにはバケットの中身にアクセスする権限しか与えられていません。バケット名一覧を取得する権限は与えてないのです。
そのため、エラーになっていました。
まとめ
AWScala、完成度がそんなに高くない気がしていて、結構罠がある気がするので、気をつけたほうがよさそうです。