mirror of
https://github.com/jkaninda/go-storage.git
synced 2026-03-11 03:59:03 +01:00
feat: add gsc and go_storage package
This commit is contained in:
187
s3.go
Normal file
187
s3.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package go_storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"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"
|
||||
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||
)
|
||||
|
||||
// S3Storage implements Storage interface for Amazon S3.
|
||||
type S3Storage struct {
|
||||
session *session.Session
|
||||
bucket string
|
||||
region string
|
||||
}
|
||||
|
||||
// NewS3Storage creates a new S3 storage instance.
|
||||
func NewS3Storage(config Config) (*S3Storage, error) {
|
||||
if err := validateConfig(config, map[string]string{
|
||||
"bucket": config.S3Bucket,
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("s3: %w", err)
|
||||
}
|
||||
|
||||
awsConfig := aws.NewConfig()
|
||||
|
||||
if config.S3Region != "" {
|
||||
awsConfig = awsConfig.WithRegion(config.S3Region)
|
||||
}
|
||||
|
||||
if config.S3KeyID != "" && config.S3Secret != "" {
|
||||
awsConfig = awsConfig.WithCredentials(
|
||||
credentials.NewStaticCredentials(config.S3KeyID, config.S3Secret, ""),
|
||||
)
|
||||
}
|
||||
|
||||
if config.S3Endpoint != "" {
|
||||
awsConfig = awsConfig.WithEndpoint(config.S3Endpoint)
|
||||
}
|
||||
|
||||
if config.S3ForcePath {
|
||||
awsConfig = awsConfig.WithS3ForcePathStyle(true)
|
||||
}
|
||||
|
||||
if config.S3DisableTLS {
|
||||
awsConfig = awsConfig.WithDisableSSL(true)
|
||||
}
|
||||
|
||||
sessionOpts := session.Options{
|
||||
Config: *awsConfig,
|
||||
SharedConfigState: session.SharedConfigEnable,
|
||||
}
|
||||
|
||||
if config.S3Profile != "" {
|
||||
sessionOpts.Profile = config.S3Profile
|
||||
}
|
||||
|
||||
sess, err := session.NewSessionWithOptions(sessionOpts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("s3: failed to create session: %w", err)
|
||||
}
|
||||
|
||||
return &S3Storage{
|
||||
session: sess,
|
||||
bucket: config.S3Bucket,
|
||||
region: config.S3Region,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Upload uploads a file to S3.
|
||||
func (s *S3Storage) Upload(ctx context.Context, localPath string, remotePath string) error {
|
||||
file, err := os.Open(localPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("s3: failed to open local file: %w", err)
|
||||
}
|
||||
defer func(file *os.File) {
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
fmt.Printf("s3: warning: failed to close file: %v\n", err)
|
||||
}
|
||||
}(file)
|
||||
|
||||
uploader := s3manager.NewUploader(s.session)
|
||||
|
||||
_, err = uploader.UploadWithContext(ctx, &s3manager.UploadInput{
|
||||
Bucket: aws.String(s.bucket),
|
||||
Key: aws.String(normalizePathSeparators(remotePath)),
|
||||
Body: file,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("s3: failed to upload to bucket %s: %w", s.bucket, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Download downloads a file from S3.
|
||||
func (s *S3Storage) Download(ctx context.Context, remotePath string, localPath string) error {
|
||||
file, err := os.Create(localPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("s3: failed to create local file: %w", err)
|
||||
}
|
||||
defer func(file *os.File) {
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
fmt.Printf("s3: warning: failed to close file: %v\n", err)
|
||||
}
|
||||
}(file)
|
||||
|
||||
downloader := s3manager.NewDownloader(s.session)
|
||||
|
||||
_, err = downloader.DownloadWithContext(ctx, file, &s3.GetObjectInput{
|
||||
Bucket: aws.String(s.bucket),
|
||||
Key: aws.String(normalizePathSeparators(remotePath)),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("s3: failed to download from bucket %s: %w", s.bucket, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// List lists objects in S3 with the given prefix.
|
||||
func (s *S3Storage) List(ctx context.Context, prefix string) ([]Item, error) {
|
||||
svc := s3.New(s.session)
|
||||
items := make([]Item, 0)
|
||||
|
||||
var continuationToken *string
|
||||
|
||||
for {
|
||||
input := &s3.ListObjectsV2Input{
|
||||
Bucket: aws.String(s.bucket),
|
||||
Prefix: aws.String(normalizePathSeparators(prefix)),
|
||||
ContinuationToken: continuationToken,
|
||||
}
|
||||
|
||||
resp, err := svc.ListObjectsV2WithContext(ctx, input)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("s3: failed to list objects in bucket %s: %w", s.bucket, err)
|
||||
}
|
||||
|
||||
for _, obj := range resp.Contents {
|
||||
items = append(items, Item{
|
||||
Key: *obj.Key,
|
||||
ModifiedTime: *obj.LastModified,
|
||||
Size: *obj.Size,
|
||||
IsDirectory: false,
|
||||
})
|
||||
}
|
||||
|
||||
if resp.IsTruncated == nil || !*resp.IsTruncated {
|
||||
break
|
||||
}
|
||||
|
||||
continuationToken = resp.NextContinuationToken
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// Remove deletes an object from S3.
|
||||
func (s *S3Storage) Remove(ctx context.Context, remotePath string) error {
|
||||
svc := s3.New(s.session)
|
||||
|
||||
_, err := svc.DeleteObjectWithContext(ctx, &s3.DeleteObjectInput{
|
||||
Bucket: aws.String(s.bucket),
|
||||
Key: aws.String(normalizePathSeparators(remotePath)),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("s3: failed to remove object from bucket %s: %w", s.bucket, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close releases resources.
|
||||
func (s *S3Storage) Close() error {
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user