📷
Golang x Lambdaで画像をS3へアップロードする
Golang on AWS Lambdaで、API GatewayからBase64形式の画像データを受け取って画像ファイルに変換した上でS3へアップロードする方法をまとめました。
この記事の説明で使っているコードは以下に掲載していますので、ご自由にお使いください。
yu-croco/golang_lambda_s3_sample
*serverless frameworkを使っています
実装
1. API Gatewayからデータを受け取る
POSTリクエストのAPI GatewayからBodyを取得するためには、リクエストBodyをbyteに変換した上でjson unmarshalで変換したい構造体を指定することでデータを取得できます。
*もっといい方法あるかもしれません。
ナイーブな実装としては以下の感じでしょうか
リクエストBodyの変換
func Handler(apiRequest events.APIGatewayProxyRequest) error {
jsonBytes := []byte(requestBody)
req := new(model.Request)
if unmarshalErr := json.Unmarshal(jsonBytes, req); unmarshalErr != nil {
return nil, unmarshalErr
}
...
}
2. Base64形式の画像データを処理する
構造体に変換したデータを用いてBase64形式の画像データをjpg形式に変換し、変換したデータをLambdaのローカルに一時保存します。
base64.StdEncoding.DecodeString
を使ってデコードして、特定のファイル名でファイルを作成して保存する感じです。今回は/tmp
配下にjpgファイルを作成しています。
元データをデコードしてローカルに保存
type localFileRepositoryImpl struct {
Request *model.Request
}
func NewLocalFileRepositoryImpl() domain.LocalFileRepository {
return &localFileRepositoryImpl{}
}
func (impl *localFileRepositoryImpl) Add(req *model.Request, jstCurrentUnixTime int) error {
data, decodeErr := base64.StdEncoding.DecodeString(req.Image)
if decodeErr != nil {
return decodeErr
}
file, err := os.Create(util.LambdaLocalFilePath(jstCurrentUnixTime))
if err != nil {
return err
}
defer file.Close()
file.Write(data)
return nil
}
3. S3へ画像をアップロードする
aws-sdk-go
を使ってS3へ画像をアップロードします。先程作成したjpgファイルのパスを指定してS3へuploadします。
アップロードした画像がpublicで問題ない場合には、ACL
をpublic-read
にしておきます。これでアップロード完了後すぐにリソースを読み込めます。
Lambda側ではS3へのアクセス権限を付与することをお忘れなく。
S3へ画像をアップロード
type s3RepositoryImpl struct{}
func NewS3RepositoryImpl() domain.S3Repository {
return s3RepositoryImpl{}
}
func (impl s3RepositoryImpl) Add(req *model.Request, jstCurrentUnixTime int) error {
file, openErr := os.Open(util.LambdaLocalFilePath(jstCurrentUnixTime))
if openErr != nil {
return openErr
}
defer file.Close()
if uploadErr := impl.upload(req, file, jstCurrentUnixTime); uploadErr != nil {
return uploadErr
}
return nil
}
func (impl s3RepositoryImpl) upload(req *model.Request, file *os.File, jstCurrentUnixTime int) error {
uploader := s3manager.NewUploader(impl.newSession())
if _, err := uploader.Upload(&s3manager.UploadInput{
Bucket: aws.String(os.Getenv("BUCKET_NAME")),
Key: aws.String(util.S3FilePath(req, jstCurrentUnixTime)),
Body: file,
ACL: aws.String("public-read"),
}); err != nil {
return err
}
return nil
}
func (impl s3RepositoryImpl) newSession() *session.Session {
return session.Must(session.NewSession(&aws.Config{
Region: aws.String("ap-northeast-1"),
}))
}
Discussion