Goを使ってS3にファイルを一括アップロードする
Amazon S3
Amazon S3は、ウェブ上のどこからでもファイルを保存し、取り出すことができるAmazonのストレージです。Amazonが提供する、拡張性、信頼性、高速性、安価性に優れたストレージになります。ファイルの保存と取り出しのための使いやすい開発キットを提供されています。
今回は、S3のバケットにgolangを使って、大量のファイルをアップロードしてみたいと思います。S3にファイルをアップロードするために、Go AWS SDKを使用します。(AWS SDK for Goは、Go 1.15以降のバージョンが必要です)
AWS Golang SDKをダウンロードします。
$ go get github.com/aws/aws-sdk-go/aws
main.goファイルを作成し、必要なパッケージをインポートして、S3バケットにファイルをアップロードします。
package main
import (
"bytes"
"fmt"
"log"
"net/http"
"os"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
)
Session構造体を使うので、このレシーバをメインのアップロード関数に渡します。
type Session struct {
S3Session *session.Session
}
AWSセッションをセットアップする
S3 REGIONとSECRET ID、SECRET KEYを使って設定を行い、複数のファイルをS3にアップロードするためのAWSセッションを1つ作成します。そして、upload()
メソッドを呼び出し、AWSセッションインスタンスとファイルの詳細を渡し、S3サーバーにファイルをアップロードします。
func main() {
paths := []string{"", ""}
credential := credentials.NewStaticCredentials(
os.Getenv("SECRET_ID"),
os.Getenv("SECRET_KEY"),
"",
)
awsConfig := aws.Config{
Region: aws.String(os.Getenv("REGION")),
Credentials: credential,
}
s, err := session.NewSession(&awsConfig)
if err != nil {
log.Println("failed to create S3 session:", err.Error())
return
}
se := Session{s}
err = se.upload(paths)
if err != nil {
log.Println(err.Error())
return
}
}
まずos.Getenv()
を使ってすべての設定情報を読み込み、aws sdkの関数credentials.NewStaticCredentials()
に渡しています。またaws.Config構造体を呼び出してその中のcredential変数を渡し、環境変数からREGION値も読み込んでいます。
aws.Config型のawsConfigをsession.NewSession()
に渡し、awsのs3セッションを返すことで、1つのセッションで複数のファイルをアップロードできるようにしています。
Session構造体にセッションを渡し、その後、uploadメソッドを呼び出して、アップロードする必要があるすべてのファイルのパスとセッション情報を渡しています。
S3にファイルを一括アップロードするためのアップロードメソッドを作成する
S3にファイルをアップロードするためのメソッドupload()
を作成します。ファイルを1つずつ開いてバッファに格納し、S3からPutObject()
メソッドを使用してS3にファイルを配置します。ファイルをアップロードする際に、PutObjectInput
にオプションを指定します。s3.PutObjectInput
構造体のServerSideEncryption
オプションを使用して、ファイルのAES256暗号化を有効にします。
func (s Session) upload(paths []string) error {
for _, path := range paths {
upFile, err := os.Open(path)
if err != nil {
log.Printf("failed %s, error: %v", path, err.Error())
continue
}
defer upFile.Close()
upFileInfo, err := upFile.Stat()
if err != nil {
log.Printf("failed to get stat %s, error: %v", path, err.Error())
continue
}
var fileSize int64 = upFileInfo.Size()
fileBuffer := make([]byte, fileSize)
upFile.Read(fileBuffer)
// uploading
_, err = s3.New(s.S3Session).PutObject(&s3.PutObjectInput{
Bucket: aws.String(os.Getenv("BUCKET_NAME")),
Key: aws.String(path),
ACL: aws.String("public-read"),
Body: bytes.NewReader(fileBuffer),
ContentLength: aws.Int64(int64(fileSize)),
ContentType: aws.String(http.DetectContentType(fileBuffer)),
ContentDisposition: aws.String("attachment"),
ServerSideEncryption: aws.String("AES256"),
StorageClass: aws.String("INTELLIGENT_TIERING"),
})
if err != nil {
log.Printf("failed to upload %s, error: %v", path, err.Error())
continue
}
url := "https://%s.s3-%s.amazonaws.com/%s"
url = fmt.Sprintf(url, os.Getenv("BUCKET_NAME"), os.Getenv("REGION"), path)
fmt.Printf("Uploaded File Url %s\n", url)
}
return nil
}
Sessionをレシーバとするupload()
というメソッドを作成しました。Session構造体はmain関数で作成したawsセッションで、upload()
メソッドにはアップロードする必要があるすべてのファイルも渡しています。
upload()
メソッドでは、path変数にループをかけ、ループ内でまずos.Open
を呼び出し、ファイルを開いた後にStat
メソッドを呼び出し、aws s3 sdkメソッドに渡す必要がある、開いたファイルに関するすべての情報を取得しています。
s3.PutObjectInput
に渡す情報は、パス、ファイルサイズ、ファイルバッファのみで、s3バケットにファイルをアップロードするための有効なhttpリクエストを作成するために使用することが可能となっています。
Discussion