Google App Engine の Images API とは何か（＋Go のサンプル実装）

## Images API とは？ 
![image.png](https://qiita-image-store.s3.amazonaws.com/0/10713/472e4707-3b45-6217-b025-6cd71e1b1dca.png)

Cloud Storage に保管された画像に対して、パラメータによる動的なリサイズや切り抜きを行える公開 URL を発行してくれるものです。

自前で画像のリサイズ処理を書かなくても、簡単にサムネイル用の画像などが配信できて非常に便利です。

## Serving URL 画像処理パラメータ
URLの末尾に `=s32` のような文字列をつけることで、動的に処理された画像を取得できます。

| 処理 | パラメータ例 |
|:----|:--------|
|**幅32px** に収まるようにリサイズ | `http://lh*.googleusercontent.com/xxxxxxx=s32`  |
|**32x32px** の正方形にリサイズ + 切り抜き | `http://lh*.googleusercontent.com/xxxxxxx=s32-c` |

## Go で実装してみる
Cloud Storage に保存されているファイルの Serving URL を生成する最小限の Google App Engine 実装を Go で書いてみました。

```go
package main

import (
	"context"
	"fmt"
	"google.golang.org/appengine"
	"google.golang.org/appengine/blobstore"
	"google.golang.org/appengine/image"
	"net/http"
)

func init() {
	http.HandleFunc("/", handler)
}

func handler(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)
	// Cloud Storage のファイルの情報を次の2変数に入れる
	cloudStorageBucketID := "<BucketID>"
	cloudStorageObjectID := "<ObjectID>"

	servingURL, err := generateServingUrl(ctx, cloudStorageBucketID, cloudStorageObjectID)
	if err != nil {
		fmt.Fprintf(w, "[error!] %s", err)
		return
	}
	fmt.Fprintf(w, "Serving URL: %s", servingURL)
}

// Serving URL を生成する
func generateServingUrl(ctx context.Context, bucketID, objectID string) (string, error) {
	// Cloud Storage にあるファイルの指定方法は /gs/<bucketID>/<objectID> で決まっています
	// https://cloud.google.com/appengine/docs/standard/go/blobstore/reference#BlobKeyForFile
	gsURL := fmt.Sprintf("/gs/%s/%s", bucketID, objectID)

	// Cloud Storage の指定から Serving URL 生成に渡すためには blobstore key 形式で渡す必要があります
	// https://cloud.google.com/appengine/docs/standard/go/images/#serving_images_from_cloud_storage_using_the_blobstore_api
	blobKey, err := blobstore.BlobKeyForFile(ctx, gsURL)
	if err != nil {
		return "", err
	}

	// Serving URL を生成
	servingURLOpts := &image.ServingURLOptions{Secure: true}
	url, err := image.ServingURL(ctx, blobKey, servingURLOpts)
	if err != nil {
		return "", err
	}
	return url.String(), nil
}
```

このコードを App Engine にデプロイ後、アクセスすると Serving URL がブラウザに出力されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/10713/98d146d7-f874-b2f2-a197-0651147aae31.png)


末尾に `=s200` のようにパラメータをつけて、リサイズされるか試してみましょう！ 

![image.png](https://qiita-image-store.s3.amazonaws.com/0/10713/96dc25cf-da31-e688-8764-6e13c7265c77.png)


## 注意点

### Images API が使えるのは App Engine 上のみ
Cloud Storage API から直接できそうな気もしたのですが、**Images API は AppEngine の APIであり、Cloud Storage API ではない** ので無理でした。

Cloud Storage 側にも Images API 相当の機能を提供してほしいという意見はあるみたいです。今後に期待！

- [Provide image serving+resizing outside of standard environment - Google Issue Tracker](https://issuetracker.google.com/issues/35901053)
- [Get Serving URL for images stored in Storage (Similar to App Engine Image Class) · Issue #1295 · GoogleCloudPlatform/google-cloud-python](https://github.com/GoogleCloudPlatform/google-cloud-python/issues/1295)


### Serving URL は誰でもアクセスできる
生成される URL はランダム文字列がついているので、推測は難しいですが、URL自体は誰でもアクセスできる状態になっています。
生成元の cloud storage オブジェクトが非公開であっても、Serving URL の画像は常に公開状態となります。

### ローカル開発サーバーでは正常な Serving URL が生成できない
`goapp serve` などのローカル開発サーバーを使うと、正常な Serving URL は生成されず次のような仮のURLとなります。

```
http://localhost:8080/_ah/img/encoded_gs_file:PEJ1Y2tldElEPi88T2JqZWN0SUQ-
```

## 参考サイト

- [Images Go API Overview](https://cloud.google.com/appengine/docs/standard/go/images/)
- [Uploading, Resizing and Serving images with Google Cloud Platform](https://medium.com/google-cloud/uploading-resizing-and-serving-images-with-google-cloud-platform-ca9631a2c556)
- [GCP で画像ファイルを動的リサイズする](https://qiita.com/seizans/items/c71d127780289867c27d)