2 minutes
Upload a directory to AWS S3 with Go
While working on this I had some difficulty finding exactly what I needed so, here’s this post for whoever may need it.
The function comment is quite clear on what the function does. It uploads the content of a directory into an AWS S3 bucket.
Some details
LoadDefaultConfig
is loading the AWS CLI config from the default path ~/.aws
.
awsConfig, err := config.LoadDefaultConfig(context.TODO())
It makes use of an errgroup
to ease goroutine synchronization and error propagation. Since this function was created as part of the internals of a CLI/API, I wanted to be able to get an error from it instead of a panic
/os.Exit()
/log.Fatal()
and leave it to the top layers to handle it as they see fit.
errGrp := new(errgroup.Group)
Walk the file tree rooted at sourcePath
. Skip the upload of directories because only files can be uploaded to S3. In the end the tree will be the same because of the objectKey
(see function comment doc).
_ = filepath.Walk(sourcePath, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
The entire thing
// UploadToBucket uploads a the contents of a directory into a bucket
// where rootKey is the root of every object.
// sourcePath is the path to the directory to upload
// UploadToBucket assumes the last part of the sourcePath to be the root of the object key.
// Example:
// - /home/bugsbunny/.acme/carrots
// - ./red_carrot.txt
// - ./favs/red_carrot.txt
//
// UploadToBucket("/home/bugsbunny/.acme/carrots") will upload the txt files with the following keys:
// - carrots/red_carrot.txt
// - carrots/favs/red_carrot.txt
//
func UploadToBucket(sourcePath string) error {
var bucket = viper.GetString("aws_s3_bucket")
awsConfig, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
return err
}
client := s3.NewFromConfig(awsConfig)
rootKey := path.Base(sourcePath)
pathPrefix := strings.TrimSuffix(sourcePath, "/")
pathPrefix = strings.TrimSuffix(sourcePath, rootKey)
errGrp := new(errgroup.Group)
_ = filepath.Walk(sourcePath, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
errGrp.Go(func() error {
objectKey := strings.TrimPrefix(path, pathPrefix)
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
if _, err = client.PutObject(context.Background(), &s3.PutObjectInput{
Bucket: &bucket,
Body: file,
Key: &objectKey,
}); err != nil {
return err
}
return nil
})
return nil
})
if err := errGrp.Wait(); err != nil {
return err
}
return nil
}
372 Words
2021-02-06