GAE/Go で静的画像リサイズ・変換をやった話
GAE/Goを動的画像リサイズ・変換サーバとして動かす情報はあるが, 静的画像リサイズ・変換をGAE/Goでやる情報が見つけられなかったのでまとめておく。
実現したい内容
一覧で表示する場合は小さいサイズの画像, 詳細画面で見る場合は大きいサイズの画像で表示したい。
実際には動的画像変換サーバを置くのが一番良いのだが, 実運用しているプロダクトだったり様々な事情があったので今回は静的に画像変換をしCloud Storageにアップロードすることにした。
注意点
GAEだとローカルにファイルを保存することができない。
一時ファイルを使ってそのファイルに変換した画像を保存するような処理ができない。
全てオンメモリで処理するしかない
実装内容
実際の処理は大きくこんな感じ。
- Cloud Storageに保存している画像へのurlにGetリクエストを送りレスポンスを取得する。
- レスポンスを
image
にDecodeする。 -
image
をリサイズ・変換する。 -
image
をbyte[]
に変換する。 - bucketにアップロードする
importしたパッケージ
import (
"bytes"
"context"
"fmt"
"image"
"image/gif"
"image/jpeg"
"image/png"
"io"
"io/ioutil"
"time"
"cloud.google.com/go/storage"
"github.com/nfnt/resize"
"github.com/pkg/errors"
"google.golang.org/appengine"
"google.golang.org/appengine/log"
"google.golang.org/appengine/urlfetch"
)
1. Cloud Storageに保存している画像へのurlにGetリクエストを送りレスポンスを取得する。
client := urlfetch.Client(c)
resp, err := client.Get(originalImageUrl)
GAE/Go httpでGetリクエストを送りたい場合は urlfetch
パッケージを使用する必要があります。
func Client(ctx context.Context) *http.Client
帰ってきた *http.Client
を使ってGetリクエストを送ります。
2. レスポンスをimageにDecodeする。
originalImage, format, err := image.Decode(resp.Body)
func Decode(r io.Reader) (Image, string, error)
デコードとformatを返してくれます。
このformatをcontentTypeの指定に使います。
3. imageをリサイズ・変換する。
smallImage := resize.Thumbnail(150, 150, original, resize.Lanczos3)
func Thumbnail(maxWidth, maxHeight uint, img image.Image, interp InterpolationFunction) image.Image
widthとheightとベースとなるimageとリサイズする際のポリシーを指定すると, アスペクト比を保ったままリサイズしてくれる。
4. image
を byte[]
に変換する。
storageClientで書き込む場合に引数がbyte[]
なので imageを変換する必要がある。
buf := new(bytes.Buffer)
err := encodeImage(buf, image, format)
if err != nil {
return nil, errors.Wrap(err, "Failed - Encode Error")
}
return buf.Bytes(), nil
func encodeImage(target io.Writer, imageData image.Image, imageFormat string) error {
switch imageFormat {
case "jpeg", "jpg":
jpeg.Encode(target, imageData, nil)
case "png":
png.Encode(target, imageData)
case "gif":
gif.Encode(target, imageData, nil)
default:
return errors.New("invalid format")
}
return nil
}
- 新しいバッファーを作成する。
- formatによって使うPackageのEncodeを分ける。
- Encodeした結果をバッファーに保存する。
- バッファの内容を
byte[]
で返す。
5. bucketにアップロードする
storageClient, _ := storage.NewClient(c)
blobWriter := storageClient.Bucket(bucketName).Object(fileName).NewWriter(c)
blobWriter.ContentType = contentType
_, err := blobWriter.Write(bytes)
if err != nil {
return "", errors.Wrap(err, "Failed - Write Error")
}
if err := blobWriter.Close(); err != nil {
return "", errors.Wrap(err, "Failed - WriterClose Error")
}
こんな感じでアップロードできる。
ちなみにアップロードができたかの判断はblobWriter.Close()
でやることが推奨されている。
Close completes the write operation and flushes any buffered data. If Close doesn't return an error, metadata about the written object can be retrieved by calling Attrs.
アップロードができた場合は func(w * Writer)Attrs()* ObjectAttrs
でメタデータの確認もできる。
最後に
GAE/Go で一時的なtmpファイルを作らなくてもリサイズ・アップロードはできたが動的画像変換サーバを使うのがベストや(笑)
複数サービスに展開したりするのも大変だし, オリジナルの画像をいじって複数枚保存するのは要領が悪いように見える。
Author And Source
この問題について(GAE/Go で静的画像リサイズ・変換をやった話), 我々は、より多くの情報をここで見つけました https://qiita.com/takpy/items/0979ab9ca29660b90b63著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .