LoginSignup
12
10

More than 5 years have passed since last update.

goamz と goroutine を使って S3 へ非同期アップロードする + Signed URL の取得

Last updated at Posted at 2014-12-11

S3ファイルをアップロードして Signed URL (時間制限でアクセス可能なURL)を取得してみます。

S3にアップロードされたオブジェクトは(IAMアカウント等の設定を行わない限り)基本的に全公開か非公開となり、オブジェクト単位での公開/非公開の設定はできません。

しかし、Signed URL を取得することによって、時間制限付きで任意のオブジェクトを公開することができます。
今回は Go の goamz ライブラリを使ってファイルのアップロードから Signed URL の取得までを行ってみます。

仕様

  • 同じ階層にある images ディレクトリの中の画像ファイルをS3へアップロードする
  • ファイル名は SHA256 のハッシュ値に変換する
  • Signed URL を構造体に詰めて返す

コード

package main

import (
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "path/filepath"
    "strings"
    "sync"
    "time"

    "github.com/goamz/goamz/aws"
    "github.com/goamz/goamz/s3"
)

type SignedURLResponse struct {
    URL string
}

var image_exts = []string{
    "bmp",
    "gif",
    "jpeg",
    "png",
    "tiff",
}

func IsImageFile(file []byte) (bool, string) {
    mime := http.DetectContentType(file)
    s := strings.Split(mime, "/")

    if len(s) < 2 {
        return false, ""
    }

    for _, ext := range image_exts {
        if ext == s[1] {
            return true, mime
        }
    }
    return false, ""
}

func GenFileHash(file []byte) string {
    h := sha256.New()
    h.Write(file)
    bs := h.Sum(nil)

    return hex.EncodeToString(bs)
}

func S3Upload(bucket *s3.Bucket, dir string) {
    wg := new(sync.WaitGroup)
    fs, _ := ioutil.ReadDir(dir)

    for _, f := range fs {
        wg.Add(1)
        go func(inner_f os.FileInfo) {
            buf := make([]byte, inner_f.Size())
            img, _ := os.Open(dir + "/" + inner_f.Name())
            defer img.Close()
            img.Read(buf)

            is_image, mime := IsImageFile(buf)
            if is_image {
                filename := GenFileHash(buf) + filepath.Ext(inner_f.Name())
                fmt.Println("Uploading .. " + filename)

                err := bucket.Put(filename, buf, mime, s3.BucketOwnerFull, s3.Options{})
                if err != nil {
                    log.Fatal(err)
                }
            }
            wg.Done()
        }(f)
    }
    wg.Wait()
}

func GetSignedURL(bucket *s3.Bucket) []SignedURLResponse {
    ch := make(chan SignedURLResponse)
    list, err := bucket.List("", "", "", 100)
    if err != nil {
        log.Fatal(err)
    }

    count := 0
    for _, c := range list.Contents {
        count++
        go func(inner_c s3.Key) {
            signed_url := bucket.SignedURL(inner_c.Key, time.Now().Add(time.Second*60))
            ch <- SignedURLResponse{URL: signed_url}
        }(c)
    }

    signed_urls := make([]SignedURLResponse, 0, count)
    for i := 0; i < count; i++ {
        signed_urls = append(signed_urls, <-ch)
    }

    return signed_urls
}

func main() {
    auth, err := aws.EnvAuth()
    if err != nil {
        panic(err)
    }

    s3client := s3.New(auth, aws.APNortheast)
    bucket := s3client.Bucket("test-bucket")

    S3Upload(bucket, "images")
    fmt.Println("Done")

    // SignedURL の取得
    signed_urls := GetSignedURL(bucket)
    fmt.Println(signed_urls)
}

参考文献

以下の記事を参考にしました。ありがとうございました。

12
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
10