S3 にファイルをマルチパートアップロードする CLI ツールを作った

S3 にファイルをマルチパートアップロードする CLI ツールを作った

作ったものはGitHubに上がっています。 s3go という S3 にファイルをマルチパートアップロードしてくれる CLI ツールを作成しました。 現状、AWS の SDK(aws-sdk-go)はマルチパートアップロードの API をサポートしていません。そのため、aws-sdk-go を使わずに一から AWS の API を利用するコードを書きました。

今回、CLI ツールを作ったのは、goroutine の学習のためです。 goroutine については書籍等読んで学習していましたが、せっかくなので実際に使いたいなと思ったのがきっかけです。 マルチパートアップロードならアップロードの処理を並列に実行できるなと思い、今回の CLI ツールを作成しました。 実施に今回の CLI ツールでは goroutine を使ってアップロードの処理を並列化しています。

マルチパートアップロードとは

ざっくりいうと、マルチパートアップロードとはファイルを小さい単位に分割し、分割されたファイルをアップロードし、それをサーバー側で結合して、1つのファイルのアップロードを行うというものです。

難しかったらところ

やはり goroutine のコードをは難しかったです。 今回はアップロードの処理を goroutine を使って並列化しました。 並列化した部分のコードは以下です。

func (s \*S3Upload) PutObject() error {
var wg sync.WaitGroup
errChan := make(chan error)
done := make(chan interface{})

    var errors error
    go func() {
    	for err := range errChan {
    		if err != nil {
    			errors = multierror.Append(errors, err)
    		}
    	}
    	defer close(done)
    }()

    for n := range s.fileSlice {
    	wg.Add(1)
    	go func(n int) {
    		s.PutMultiPartObject(n+1, errChan)
    		defer wg.Done()
    	}(n)
    }

    wg.Wait()
    close(errChan)
    <-done
    return errors

}

今回は sync.WaitGroup を利用して goroutine の制御を行いました。 sync.WaitGroup は wg.Add(1)をすると内部変数がインクリメントされ、wg.Done()を実行するとデクリメントされます。 wg.Wait()は内部変数が 0 になるまで処理を待ちます。 goroutine で関数を実行し、goroutine で実行した関数が終了するときにdefarwg.Done()を呼び出します。 sync.WaitGroup の Wait メソッドを利用することで、goroutine が全て終了したことを確認し、安全に関数を終了できます。

gorutine で実行している関数には err チャネルと引数で渡します。 err チャネルには関数内で起きたエラーが送信されてきます。 err チャネルからエラーを読みとる関数を goroutine で実行します。 goroutine で読み取らないと error をチャネルに書き込めずにデッドロックを起こします。 err チャネル経由できた error は hashicoro/go-multierrorr を使って一つの error にまとめます。

まとめ

定期的に何かを作って今後もブログ書きたいと思います。