🚗

GoでS3アップロード実装とバリデーション

2024/12/14に公開

はじめに

最近はバックエンドのS3への画像ファイルアップロード関連について行っていました。本記事では、S3アップロード処理の基本と、Go/Ginでの具体的なバリデーション実装方法について解説します。

HowTelevision Advent Calendar 2024 の 12月14日の記事です。

https://qiita.com/advent-calendar/2024/howtv

実装

使用ライブラリ

  • gin
  • aws-sdk-go-v2
  • goの標準ライブラリ

リクエストデータのバリデーション

Controller層でリクエストデータに画像ファイルが含まれているかを確認しています。ginを用いた例が以下です。

form, err := c.MultipartForm()
if err != nil {
  return FileInput{}, err
}
var image *multipart.FileHeader
if len(form.File["image"]) > 0 {
  image = form.File["image"][0]
}
if image == nil {
  return FileInput{}, fmt.Errorf("image is required")
}

アップロードファイルのバリデーション

対象のファイルが要件を満たしているか確認します。例として以下の条件でバリデーションします。

  • ファイルサイズが5MB以下
  • 画像の解像度が指定サイズ(640×100)に一致
  • ファイル形式が PNG, JPEG, GIF のいずれか
const maxUploadFileSize = 5 * 1024 * 1024 // 5MB
const maxImageWidth = 640
const maxImageHeight = 100

func validateFile(file *multipart.FileHeader) error {
	// ファイルサイズチェック
	if file.Size > maxUploadFileSize {
		return fmt.Errorf("file size is too large")
	}

	// 画像サイズチェック
	width, height, err := getImageDimensions(file)
	if err != nil {
		return fmt.Errorf("failed to get image dimensions: %w", err)
	}
	if width != maxImageWidth || height != maxImageHeight {
		return fmt.Errorf("image size is invalid (width: %d, height: %d)", width, height)
	}

	// 形式チェック
	contentType := file.Header.Get("Content-Type")
	if contentType != "image/png" && contentType != "image/jpeg" && contentType != "image/gif" {
		return fmt.Errorf("unsupported file type: %s", contentType)
	}
	return nil
}

不適切な形式を排除するようにしてS3ストレージを安全に利用します。

ファイルをバイト配列に変換

ファイルの型を実際にアップロード可能な型に変換する必要があります。

func fileToBytes(file *multipart.FileHeader) ([]byte, error) {
	f, err := file.Open()
	if err != nil {
		return nil, err
	}
	defer f.Close()

	bu := bytes.NewBuffer(nil)
	if _, err = io.Copy(bu, f); err != nil {
		return nil, err
	}

	return bu.Bytes(), nil
}

アップロードデータの管理

アップロードファイルに関連するメタデータを構造体で定義しアップロード後の関連処理に使用します。アップロードファイルと関連データについては複雑になり得るため型定義をしておくことで可読性と保守性を保ちます。

type UploadFileOutput struct {
	Name string
	Dir  string
	Size int64
	Type string
}

type FileInput struct {
	Image             *multipart.FileHeader `json:"image" validate:"required"`
	UploadedImageFile UploadFileOutput
	ItemID            int    `form:"ItemID" validate:"required"`
}

最後に

S3へのアップロード処理を安全かつ効率的に実装するには、リクエストデータのバリデーション、データ変換、アップロード後のデータ管理、エラーハンドリングが重要です。S3アップロード処理の参考になれば幸いです。

参考

https://docs.aws.amazon.com/ja_jp/code-library/latest/ug/go_2_s3_code_examples.html

Discussion