/
s3.go
122 lines (110 loc) · 3.05 KB
/
s3.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package s3
import (
"context"
"fmt"
"io"
"path"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"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"
"github.com/jotfs/jotfs/internal/store"
)
// Config stores the configuration for the S3 store.
type Config struct {
Region string
Endpoint string
AccessKey string
SecretKey string
PathStyle bool
DisableSSL bool
}
// Store implements the Store interface for an S3-compatible backend.
type Store struct {
cfg Config
svc *s3.S3
}
// New creates a new client for accessing an S3-backed store.
func New(cfg Config) (*Store, error) {
acfg := aws.Config{
Endpoint: &cfg.Endpoint,
S3ForcePathStyle: &cfg.PathStyle,
DisableSSL: &cfg.DisableSSL,
Region: &cfg.Region,
}
if cfg.AccessKey != "" {
acfg.Credentials = credentials.NewStaticCredentials(cfg.AccessKey, cfg.SecretKey, "")
} else {
fmt.Println("Using shared credentials")
acfg.Credentials = credentials.NewSharedCredentials("", "")
}
sess, err := session.NewSession(&acfg)
if err != nil {
return nil, err
}
svc := s3.New(sess)
return &Store{cfg, svc}, nil
}
// Put saves an object to S3.
func (s *Store) Put(ctx context.Context, bucket string, key string, r io.Reader) error {
uploader := s3manager.NewUploaderWithClient(s.svc, func(u *s3manager.Uploader) {
u.Concurrency = 1
})
_, err := uploader.UploadWithContext(ctx, &s3manager.UploadInput{
Body: r,
Bucket: &bucket,
Key: &key,
})
return err
}
// Get returns an object from the store as an io.ReadCloser. Returns store.ErrNotFound
// if the object does not exist.
func (s *Store) Get(ctx context.Context, bucket string, key string) (io.ReadCloser, error) {
resp, err := s.svc.GetObjectWithContext(ctx, &s3.GetObjectInput{
Bucket: &bucket,
Key: &key,
})
if aerr, ok := err.(awserr.Error); ok {
if aerr.Code() == s3.ErrCodeNoSuchKey {
return nil, store.ErrNotFound
}
}
if err != nil {
return nil, err
}
return resp.Body, nil
}
// Copy makes a copy of an object.
func (s *Store) Copy(bucket string, from string, to string) error {
_, err := s.svc.CopyObject(&s3.CopyObjectInput{
Bucket: &bucket,
CopySource: aws.String(path.Join(bucket, from)),
Key: &to,
})
return err
}
// Delete removes an object. No error is returned if the object does not exist.
func (s *Store) Delete(bucket string, key string) error {
_, err := s.svc.DeleteObject(&s3.DeleteObjectInput{
Bucket: &bucket,
Key: &key,
})
return err
}
// PresignGetURL returns a URL to GET an object in the store.
func (s *Store) PresignGetURL(bucket string, key string, expires time.Duration, contentRange *store.Range) (string, error) {
var rnge *string
if contentRange != nil {
x := fmt.Sprintf("bytes=%d-%d", contentRange.From, contentRange.To)
rnge = &x
}
req, _ := s.svc.GetObjectRequest(&s3.GetObjectInput{
Bucket: &bucket,
Key: &key,
Range: rnge,
})
return req.Presign(expires)
}