GAE/Go로 정적 이미지 크기 조정 및 변환을 한 이야기

10414 단어 5GAEgcp
GAE/Go를 동적 이미지 리사이즈·변환 서버로서 움직이는 정보는 있지만, 정적 이미지 리사이즈·변환을 GAE/Go에서 하는 정보가 발견되지 않았기 때문에 정리해 둔다.

실현하고 싶은 내용



일람으로 표시하는 경우는 작은 사이즈의 화상, 상세 화면에서 보는 경우는 큰 사이즈의 화상으로 표시하고 싶다.
실제로는 동적 화상 변환 서버를 두는 것이 제일 좋지만, 실운용하고 있는 제품이거나 다양한 사정이 있었으므로 이번은 정적으로 화상 변환을 해 Cloud Storage에 업로드하기로 했다 .

주의점



GAE라면 로컬로 파일을 저장할 수 없다.
임시 파일을 사용하여 해당 파일로 변환한 이미지를 저장하는 처리를 할 수 없다.
모두 온 메모리로 처리 할 수 ​​있습니다

구현 내용



실제 처리는 크게 이런 느낌.
  • Cloud Storage에 저장된 이미지에 대한 url에 Get 요청을 보내 응답을 가져옵니다.
  • 응답을 image로 Decode합니다.
  • image 를 리사이즈 변환한다.
  • imagebyte[] 로 변환합니다.
  • bucket에 업로드

  • 가져온 패키지
    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. 이미지를 크기 조정하고 변환합니다.


        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를 분리한다.
  • 인코딩된 결과를 버퍼에 저장한다.
  • 버퍼의 내용을 byte[] 로 돌려준다.

  • 5. 버킷에 업로드


        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 파일을 만들지 않아도 크기 조정 업로드는 가능했지만 동적 이미지 변환 서버를 사용하는 것이 가장 좋습니다 (웃음).
    복수 서비스에 전개하거나 하는 것도 힘들고, 오리지날의 화상을 왕따 복수 장 보존하는 것은 요령이 나쁜 것처럼 보인다.

    좋은 웹페이지 즐겨찾기