GoでファイルのValidation方法など

### はじめに

:::note warn
こちらはzennの記事の転載となります。
:::

平下CTO@sweeepです。Go言語でよく使うファイルのValidation方法などについて解説します。
本記事の内容です。
* ファイルサイズチェック
* ファイルのValidation
* ファイル拡張子からContentTypeへの変換
* 使い方
* 実行結果


### ファイルサイズチェック
定義済みファイルサイズより大きければファイルサイズとエラーを返します。問題なければファイルサイズとnilを返します。
```Go:utils/file.go
const (
	MaxSize = 10 * 1024 * 1024 // 10MB
)

var (
	ErrFileSize   = errors.New("file size error")
)

func CheckFileSize(size int64) (int64, error) {
	if size > MaxSize { // MaxSizeより大きい場合はエラー
		return size, fmt.Errorf("size: %v error: %w", size, ErrFileSize)
	}
	return size, nil
}
```

### ファイルのValidation

Goのhttpパッケージの[DetectContentType](https://pkg.go.dev/net/http#DetectContentType)という関数を利用します。
https://pkg.go.dev/net/http#DetectContentType
以下のように*[]byteを渡して[MIMEタイプ：メディアタイプ](https://developer.mozilla.org/ja/docs/Web/HTTP/Basics_of_HTTP/MIME_types)を取得します。

```Go
mine := http.DetectContentType(*data)
```

下記はJPEG or PNGを許可する場合、取得したMIMEタイプをチェックし、該当した場合はJEPG or PNGのMINEタイプとnilを、該当しない場合は取得したMINEタイプとエラーを返します。

```Go:utils/file.go
var (
	JPEG    = "image/jpeg"
	PNG     = "image/png"
)

func ValidateFile(data *[]byte) (string, error) {
	mine := http.DetectContentType(*data)

	switch {
	case mine == JPEG: // MINEタイプimage/jpeg
		return mine, nil
	case mine == PNG: // MINEタイプがimage/png
		return mine, nil
	default:
		return mine, fmt.Errorf("mine: %v error: %w", mine, ErrValidation)
	}
}
```

### ファイル拡張子からContentTypeへの変換

HTTPヘッダーに[Content-Type](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Type)へリソースのMIMEタイプ埋めて返したい場合があると思います。
まずは以下の関数でファイル名から拡張子を取得します。

```Go:utils/file.go
func ExtractExt(name string) string {
	pos := strings.LastIndex(name, ".")

	// .から後ろをスライスで取得
	return name[pos:]
}
```

ファイルの拡張子から該当した場合はJPEG or PNGのMINEタイプを、該当しない場合は取得した拡張子を返します。

```Go:utils/file.go
var (
	JPG     = "image/jpeg"
	PNG     = "image/png"
	ExtJPG  = ".jpg"
	ExtJPEG = ".jpeg"
	ExtPNG  = ".png"
)

func ExtToContentType(ext string) (string, error) {
	switch {
	case ext == ExtJPG: // 拡張子が".jpg"の場合 ContentType：image/jpeg
		return JPEG, nil
	case ext == ExtJPEG: // 拡張子が".jpeg"の場合 ContentType：image/jpeg
		return JPEG, nil
	case ext == ExtPNG: // 拡張子が".png"の場合 ContentType：image/png
		return PNG, nil
	default:
		return ext, fmt.Errorf("extension: %v error: %w", ext, ErrNotFound)
	}
}
```

### 使い方
以下にサンプル示します。
* サンプルファイルをオープンし、ファイルサイズチェック
* ファイル拡張子からメディアタイプへ変換
* サンプルファイル読み込みしValidationの実施

```Go:main.go
import (
	"log"
	"os"

	"github.com/hirac1220/go/go-file-validate/utils/file"
)

func main() {
	// ファイルサイズチェック
	f, _ := os.Open("./sample/image.jpg") // ファイルオープン
	defer func() {
		_ = f.Close()
	}()

	fi, _ := f.Stat()
	size, err := file.CheckFileSize(fi.Size()) // ファイルサイズチェック
	if err != nil {
		log.Println(err)
	}
	log.Printf("size: %d", size)

	// ファイル拡張子からメディアタイプへ変換
	ext := file.ExtractExt(fi.Name())    // ファイル拡張子取得
	c, err := file.ExtToContentType(ext) // ファイル拡張子からメディアタイプへ変換
	if err != nil {
		log.Println(err)
	}
	log.Printf("content type: %s", c)

	// ファイルValidation
	bytes, _ := os.ReadFile("./sample/image.jpg") // ファイル読み込み
	mine, err := file.ValidateFile(&bytes)        // ファイルバリデーション
	if err != nil {
		log.Println(err)
	}
	log.Printf("mine: %s", mine)
}
```

### 実行結果
サンプルファイルに対して実行した結果は以下となります。

```
$ go run main.go
2022/02/23 17:51:29 size: 2604
2022/02/23 17:51:29 content type: image/jpeg
2022/02/23 17:51:29 mine: image/jpeg
```



### まとめ
今回はGo言語でよく使うファイルのValidation方法などについて解説しました。使用したサンプルはこちらのGitHubレポジトリにあります。

https://github.com/hirac1220/go-file-validate

ここまで読んでいただきありがとうございました！