LoginSignup
4
2

More than 3 years have passed since last update.

AWS SDK for Go v2を使ったS3のオブジェクトアップロードとダウンロードを使ってみる

Posted at

概要

  • まだRC版だけど、AWS SDK for Go v2を使ったS3のオブジェクトアップロードとダウンロードを使ってみた。
    https://github.com/aws/aws-sdk-go-v2

  • goを普段触らなけど練習で書いてみた。

前提条件

Goのバージョンがv1.15であること

$ go version
go version go1.15 linux/amd64

準備

go mod init awssdkapp

go get -u github.com/aws/aws-sdk-go-v2
go get -u github.com/aws/aws-sdk-go-v2/config
go get -u github.com/aws/aws-sdk-go-v2/feature/s3/manager
go get -u github.com/aws/aws-sdk-go-v2/service/s3
touch main.go
mkdir myclient
touch myclient/myclient.go

S3に接続するクライアントのコード

シンプルにオブジェクトをアップロードしたり、ダウンロードする。

myclient/myclient.go
package myclient

import (
    "context"
    "io"
    "log"
    "os"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/feature/s3/manager"
    "github.com/aws/aws-sdk-go-v2/service/s3"
)

type MyS3Client struct {
    downloader *manager.Downloader
    uploader   *manager.Uploader
    client     *s3.Client
}

func NewMyS3Client(cfg aws.Config) *MyS3Client {
    client := s3.NewFromConfig(cfg)
    downloader := manager.NewDownloader(client)
    uploader := manager.NewUploader(client)

    return &MyS3Client{
        downloader: downloader,
        uploader:   uploader,
        client:     client,
    }
}

// オブジェクトをダウンロードするメソッド
func (c *MyS3Client) UploadSingleObject(bucket, key string, reader io.Reader) {
    _, err := c.uploader.Upload(context.Background(), &s3.PutObjectInput{
        Bucket: aws.String(bucket),
        Key:    aws.String(key),
        Body:   reader,
    })

    if err != nil {
        log.Fatal(err)
    }

    log.Println("upload successed")
}

// オブジェクトをダウンロードするメソッド
func (c *MyS3Client) DownloadSingleObject(bucket, key, filename string) {
    file, _ := os.Create(filename)
    defer file.Close()

    _, err := c.downloader.Download(context.Background(), file, &s3.GetObjectInput{
        Bucket: aws.String(bucket),
        Key:    aws.String(key),
    })

    if err != nil {
        log.Fatal(err)
    }

    log.Println("download successed")

}
main.go
package main

import (
    "context"
    "flag"
    "log"
    "strings"

    "awsdkapp/myclient"

    "github.com/aws/aws-sdk-go-v2/config"
)

func main() {

    bucket := flag.String("bucket", "", "specify bucket name")
    key := flag.String("key", "", "specify key name ")
    flag.Parse()

    if *bucket == "" || *key == "" {
        log.Fatal("error invalid arugments")
    }

    // コンフィグの作成
    cfg, err := config.LoadDefaultConfig(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    client := myclient.NewMyS3Client(cfg)

    // コンテンツをアップロードする
    client.UploadSingleObject(*bucket, *key, strings.NewReader("hello, aws sdk"))

    // ファイルとしてダウンロードする
    client.DownloadSingleObject(*bucket, *key, "hello.txt")
}

実行してみると、アップロード/ダウンロードできるはず。

$ go run main.go -bucket the-chocomintkusoyaro-demo -key hoge
2020/12/30 23:05:32 upload successed
2020/12/30 23:05:32 download successed

$ ls hello.txt 
hello.txt

$ cat hello.txt 
hello, aws sdk

複数のオブジェクトをダウンロードする場合

NewListObjectsV2Paginatorでページネータを生成してページ単位でオブジェクト群を取得して、オブジェクト一つ一つをダウンロード書き込みする。

myclient/myclient.go
...

// 複数のオブジェクトをダウンロードする
func (c *MyS3Client) DownloadMultipleObjects(bucket, prefix, localDir string) {

    // ページネーターを作成する
    paginator := s3.NewListObjectsV2Paginator(c.client, &s3.ListObjectsV2Input{
        Bucket: aws.String(bucket),
        Prefix: aws.String(prefix),
    })

    for paginator.HasMorePages() {
        page, err := paginator.NextPage(context.Background())
        if err != nil {
            log.Fatal(err)
        }

        for _, obj := range page.Contents {
            if err != nil {
                log.Fatal(err)
            }

            filepath := filepath.Join(localDir, *obj.Key)

            c.DownloadSingleObject(bucket, *obj.Key, filepath)
        }
    }
    log.Println("all file downloaded")
}
main.go
package main

import (
    "context"
    "flag"
    "log"

    "awsdkapp/myclient"

    "github.com/aws/aws-sdk-go-v2/config"
)

func main() {

    bucket := flag.String("bucket", "", "specify bucket name")
    prefix := flag.String("prefix", "", "specify prefix name")
    localDir := flag.String("localDir", "", "specify local dir ")
    flag.Parse()

    if *bucket == "" || *localDir == "" {
        log.Fatal("error invalid arugments")
    }

    // コンフィグの作成
    cfg, err := config.LoadDefaultConfig(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    client := myclient.NewMyS3Client(cfg)

    // オブジェクトをダウンロードする
    client.DownloadMultipleObjects(*bucket, *prefix, *localDir)
}

複数のオブジェクトをアップロードする

ドキュメントのExampleを真似てみた。

myclient/myclient.go
...

// 複数のオブジェクトをまとめてアップロードする
func (c *MyS3Client) UploadMultipleObjects(bucket, prefix, localDir string) {
    fileWalker := make(chan string)

    // チャネルにローカルファイルパスを送信する
    go func() {
        err := filepath.Walk(localDir, func(path string, info os.FileInfo, err error) error {
            if err != nil {
                return err
            }

            if !info.IsDir() {
                // ファイルパスをチャネルに送る
                fileWalker <- path
            }

            return nil
        })

        if err != nil {
            log.Fatal(err)
        }


        // チャネルのクローズをお知らせるする
        close(fileWalker)
    }()

    // ファイルパスをチャネルから受信する
    for path := range fileWalker {
        rel, err := filepath.Rel(localDir, path)

        if err != nil {
            log.Println("unable to get relative path", path, err)
            continue
        }

        file, err := os.Open(path)
        if err != nil {
            log.Println("failed open file", path, err)
            continue
        }
        defer file.Close()

        key := filepath.Join(prefix, rel)

        // オブジェクトをダウンロードする
        c.UploadSingleObject(bucket, key, file)
    }
}
main.go
package main

import (
    "context"
    "flag"
    "log"

    "awsdkapp/myclient"

    "github.com/aws/aws-sdk-go-v2/config"
)

func main() {

    bucket := flag.String("bucket", "", "specify bucket name")
    prefix := flag.String("prefix", "", "specify prefix name")
    localDir := flag.String("localDir", "", "specify local dir ")
    flag.Parse()

    if *bucket == "" || *localDir == "" {
        log.Fatal("error invalid arugments")
    }

    // コンフィグの作成
    cfg, err := config.LoadDefaultConfig(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    client := myclient.NewMyS3Client(cfg)

    // オブジェクトをまとめてアップロードする
    client.UploadMultipleObjects(*bucket, *prefix, *localDir)
}
4
2
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
4
2