app/lib/S3Actions.scala (63 lines of code) (raw):
package lib
import java.io.File
import java.net.URI
import com.amazonaws.services.s3.AmazonS3
import com.amazonaws.services.s3.model._
import com.gu.pandomainauth.model.User
import play.api.libs.json.{JsPath, JsString, JsValue, Writes}
import play.api.libs.functional.syntax._
trait S3UploadResponse {
def url: Option[URI]
def fileName : Option[String]
def msg : Option[String]
}
case class S3UploadSuccess(url: Option[URI], fileName: Option[String], msg: Option[String])
extends S3UploadResponse
case class S3UploadFailure(url: Option[URI], fileName: Option[String], msg: Option[String])
extends S3UploadResponse
object S3UploadResponse {
def buildSuccess(putObjectRequest: PutObjectRequest, config: Config) = {
val uri = config.maybePrettyBaseUrl match {
case Some(prettyBaseUrl) =>
s"$prettyBaseUrl/${putObjectRequest.getKey}"
case None =>
s"https://s3-eu-west-1.amazonaws.com/${putObjectRequest.getBucketName}/${putObjectRequest.getKey}"
}
S3UploadSuccess(Some(new URI(uri)), Some(putObjectRequest.getKey), None)
}
implicit val writesUri: Writes[URI] = (uri: URI) => JsString(uri.toString)
implicit val s3UploadSuccessWrites: Writes[S3UploadSuccess] = (
(JsPath \ "url").writeNullable[URI] and
(JsPath \ "fileName").writeNullable[String] and
(JsPath \ "msg").writeNullable[String]
)(unlift(S3UploadSuccess.unapply))
implicit val s3UploadFailureWrites: Writes[S3UploadFailure] = (failure: S3UploadFailure) => JsString(failure.msg.getOrElse("Something went wrong"))
}
class S3Actions() {
private val s3NotFoundStatusList = List(403, 404)
def upload(file: File, user: User, config: Config, setPublicAcl: Boolean): S3UploadResponse = {
getObject(config.key(file.getName), config.bucketName, config.s3Client) match {
case None => {
val metadata = new ObjectMetadata
metadata.addUserMetadata("author", user.email)
if(setPublicAcl) { metadata.setContentType("text/html") }
val request = new PutObjectRequest(config.bucketName, config.key(file.getName), file).withMetadata(metadata)
val finalRequest = if(setPublicAcl) {
request.withCannedAcl(CannedAccessControlList.PublicRead)
} else { request }
config.s3Client.putObject(finalRequest) match {
case _: PutObjectResult => S3UploadResponse.buildSuccess(finalRequest, config)
case _ => S3UploadFailure(None, Some(file.getName), Some("Upload Failed"))
}
}
case _ => S3UploadFailure(None, Some(file.getName), Some("File with that name already exists"))
}
}
def getObject(key: String, bucketName: String, s3Client: AmazonS3) = {
try {
s3Client.getObject(bucketName, key)
} catch {
case e: AmazonS3Exception if s3NotFoundStatusList.contains(e.getStatusCode) => None
}
}
}