github.com/kubri/kubri@v0.5.1-0.20240317001612-bda2aaef967e/internal/blob/target.go (about) 1 package blob 2 3 import ( 4 "context" 5 "io" 6 "io/fs" 7 "mime" 8 "net/url" 9 "path" 10 "strings" 11 12 "gocloud.dev/blob" 13 "gocloud.dev/gcerrors" 14 15 "github.com/kubri/kubri/target" 16 ) 17 18 type blobTarget struct { 19 bucket *blob.Bucket 20 prefix string 21 baseURL string 22 } 23 24 func NewTarget(url, prefix, baseURL string) (target.Target, error) { 25 b, err := blob.OpenBucket(context.Background(), url) 26 if err != nil { 27 return nil, err 28 } 29 30 t := &blobTarget{ 31 bucket: b, 32 prefix: strings.Trim(prefix, "/"), 33 baseURL: strings.TrimRight(baseURL, "/"), 34 } 35 36 return t, nil 37 } 38 39 func (t *blobTarget) NewWriter(ctx context.Context, filename string) (io.WriteCloser, error) { 40 opt := &blob.WriterOptions{ContentType: mime.TypeByExtension(path.Ext(filename))} 41 f, err := t.bucket.NewWriter(ctx, path.Join(t.prefix, filename), opt) 42 return f, mapError("write", filename, err) 43 } 44 45 func (t *blobTarget) NewReader(ctx context.Context, filename string) (io.ReadCloser, error) { 46 f, err := t.bucket.NewReader(ctx, path.Join(t.prefix, filename), nil) 47 return f, mapError("read", filename, err) 48 } 49 50 func (t *blobTarget) Remove(ctx context.Context, filename string) error { 51 return mapError("remove", filename, t.bucket.Delete(ctx, path.Join(t.prefix, filename))) 52 } 53 54 func (t *blobTarget) Sub(dir string) target.Target { 55 return &blobTarget{bucket: t.bucket, prefix: path.Join(t.prefix, strings.Trim(dir, "/")), baseURL: t.baseURL} 56 } 57 58 func (t *blobTarget) URL(ctx context.Context, filename string) (string, error) { 59 if t.baseURL != "" { 60 return url.JoinPath(t.baseURL, t.prefix, filename) 61 } 62 63 url, err := t.bucket.SignedURL(ctx, path.Join(t.prefix, filename), &blob.SignedURLOptions{}) 64 if err != nil { 65 return "", mapError("read", filename, err) 66 } 67 68 url, _, _ = strings.Cut(url, "?") 69 return url, nil 70 } 71 72 func mapError(op, name string, err error) error { 73 switch gcerrors.Code(err) { 74 case gcerrors.OK: 75 return nil 76 case gcerrors.NotFound: 77 return &fs.PathError{Op: op, Path: name, Err: fs.ErrNotExist} 78 default: 79 return err 80 } 81 }