Go GraphQL AWS S3への画像アップロード
今回はGraphQLで画像のアップロードを行なっていきたいと思います。
アップロード先のストレージにはAWSのS3を使用します。
尚、この記事ではS3のバケットポリシーやAWS関連については深掘りしません、aws-sdk-go-v2
というGoのaws-sdkを使用しますがコード関連が中心となります。
使用技術
*このブログでは今までにも何度かgqlgenを使用した内容を紹介しており、 コードは同じレポジトリに随時追加しております。 お時間あればぜひご覧頂けると嬉しいです。
schema
まずはスキーマを定義します。 ファイルは適宜置き換えて下さい。
schema.graphql
type Mutation { uploadFile(input: UploadFileInput!): UploadFilePayload! }
file.graphql
input UploadFileInput { file: Upload! } type UploadFilePayload { uploadedPath: String! }
シンプルなMutationですが、 inputの Upload!
型に注目して下さい。
これは gqlgenの Scalar type と言って組み込みのカスタム型です。
Upload
型は以下のような構造体を呼び出します。
type Upload struct { File io.Reader Filename string Size int64 ContentType string }
Scalar typeを使用するには 任意の *.graphql
ファイル にコードを追加する必要があります。
schema.graphql
scalar Upload
これでスキーマ定義ができたので、gqlgen
コマンドで型とResolverを生成します。
*筆者はファイルを分割する為に自動生成の機能をオフにしています。
Reolver
func (r *mutationResolver) UploadFile(ctx context.Context, input model.UploadFileInput) (*model.UploadFilePayload, error) { panic(fmt.Errorf("not implemented")) }
models_gen.go
type UploadFileInput struct { File graphql.Upload `json:"file"` } type UploadFilePayload struct { UploadedPath string `json:"uploadedPath"` }
UploadFileInput
の構造体に注目して下さい。
graphql.Uploadを呼び出しています。
これが先ほど紹介した以下の構造体です。
type Upload struct { File io.Reader Filename string Size int64 ContentType string }
アップロードするファイルは この構造体としてResolverに渡されます。
次はアップロード先のs3の準備を行います。
s3の準備
今回はアップロード先にs3を使用するので事前にバケットの作成と設定をして下さい。アップロード可能な状態に設定する必要があります。
次に必要なパッケージをダウンロードします。
以下のパッケージをそれぞれ go get
して下さい。
"github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/feature/s3/manager" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/config"
sdk-v2 は Goのversion 1.15から対応になります。
aws-sdk-go-v2
は比較的新しいので aws-sdk-go
と混同しないようお気をつけ下さい。
自動インポートなどを使用すると v1 の方が呼び出されたりするので注意が必要です。
Getting Started with the AWS SDK for Go V2 | AWS SDK for Go V2
Resolverの実装
事前準備が完了したのであとは実装するのみです。 *今回はレイヤーやモジュール分割を行いません。
func (r *mutationResolver) UploadFile(ctx context.Context, input model.UploadFileInput) (*model.UploadFilePayload, error) { cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { return nil, err } client := s3.NewFromConfig(cfg) uploader := manager.NewUploader(client) res, err := uploader.Upload(ctx, &s3.PutObjectInput{ Bucket: aws.String("bucketName"), Key: aws.String("uploadPath/" + input.File.Filename), Body: input.File.File, ContentType: aws.String(input.File.ContentType), }) if err != nil { return nil, err } return &model.UploadFilePayload{ UploadedPath: res.Location, }, nil }
上から順に解説します。
こちらのコードはawsのconfigを読み込んでいます。 以下のように何もオプションを指定しないことで
cfg, err := config.LoadDefaultConfig(context.TODO())
以下の順に credential を読みに行きます。
1.Environment variables. Static Credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN) Web Identity Token (AWS_WEB_IDENTITY_TOKEN_FILE)
2.Shared configuration files. SDK defaults to credentials file under .aws folder that is placed in the home folder on your computer. SDK defaults to config file under .aws folder that is placed in the home folder on your computer.
3.If your application uses an ECS task definition or RunTask API operation, IAM role for tasks.
4.if your application is running on an Amazon EC2 instance, IAM role for Amazon EC2.
Configuring the AWS SDK for Go V2 | AWS SDK for Go V2
本番環境と開発環境でcredentialを別の場所に配置しても自動的に読みに行く場所を変更してくれるので便利ですね。
client := s3.NewFromConfig(cfg) uploader := manager.NewUploader(client) res, err := uploader.Upload(ctx, &s3.PutObjectInput{ Bucket: aws.String("bucketName"), Key: aws.String("uploadPath/" + input.File.Filename), Body: input.File.File, ContentType: aws.String(input.File.ContentType), })
あとは上記のように inputから s3に渡す構造体を整形し Uploadメソッドでアップロードします。 簡単ですね!
APIを実行
それでは実装したAPIを実行してみましょう。 恐らく現時点でgqlgenのplaygroundには ファイルのアップロード機能は搭載されていません。
その為今回は以下 firecamp というクライアントアプリを使いました。
画像下部にあるようにファイルを指定して実行します。
無事にアップロードしたpathが返ってきました! filepathをDBに保存すればフロント側から呼び出し画像表示ができますね。
今回の内容は以上です。
最後に、 このブログではweb開発について発信していくのでまたご覧頂けると嬉しいです。 最後までお読み頂きありがとうございました。