Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 2 additions & 25 deletions file.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ func (fd *reader[T]) lazyOpen() error {

fd.fs.codec.DecodeGetOutput(val, fd.info.attr)
if fd.fs.signer != nil && fd.fs.codec.s != nil {
if url, err := fd.fs.preSignGetUrl(fd.s3Key(fd.fs.root)); err == nil {
if url, err := fd.fs.preSignGetUrl(fd.s3Key(fd.fs.root), fd.fs.ttlSignedUrl); err == nil {
fd.fs.codec.s.Put(fd.info.attr, url)
}
}
Expand Down Expand Up @@ -234,29 +234,6 @@ func (fd *writer[T]) lazyOpen() {
}()
}

func (fd *writer[T]) preSignPutUrl() (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), fd.fs.timeout)
defer cancel()

req := &s3.PutObjectInput{
Bucket: aws.String(fd.fs.bucket),
Key: fd.s3Key(fd.fs.root),
Metadata: make(map[string]string),
}
fd.fs.codec.EncodePutInput(fd.attr, req)

val, err := fd.fs.signer.PresignPutObject(ctx, req, s3.WithPresignExpires(fd.fs.ttlSignedUrl))
if err != nil {
return "", &fs.PathError{
Op: "presign",
Path: fd.path,
Err: err,
}
}

return val.URL, nil
}

func (fd *writer[T]) Write(p []byte) (int, error) {
if fd.r == nil && fd.w == nil {
fd.lazyOpen()
Expand Down Expand Up @@ -300,7 +277,7 @@ func (fd *writer[T]) Stat() (fs.FileInfo, error) {
fd.info.attr = new(T)
}

if url, err := fd.preSignPutUrl(); err == nil {
if url, err := fd.fs.preSignPutUrl(fd.s3Key(fd.fs.root), fd.info.attr, fd.fs.ttlSignedUrl); err == nil {
fd.fs.codec.s.Put(fd.info.attr, url)
}
}
Expand Down
62 changes: 59 additions & 3 deletions filesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ var (
_ CreateFS[struct{}] = (*FileSystem[struct{}])(nil)
_ RemoveFS = (*FileSystem[struct{}])(nil)
_ CopyFS = (*FileSystem[struct{}])(nil)
_ CurlFS[struct{}] = (*FileSystem[struct{}])(nil)
)

// Create a file system instance, mounting S3 Bucket. Use Option type to
Expand Down Expand Up @@ -156,7 +157,7 @@ func (fsys *FileSystem[T]) Stat(path string) (fs.FileInfo, error) {
fsys.codec.DecodeHeadOutput(val, info.attr)

if fsys.signer != nil && fsys.codec.s != nil {
if url, err := fsys.preSignGetUrl(info.s3Key(fsys.root)); err == nil {
if url, err := fsys.preSignGetUrl(info.s3Key(fsys.root), fsys.ttlSignedUrl); err == nil {
fsys.codec.s.Put(info.attr, url)
}
}
Expand All @@ -174,7 +175,30 @@ func (fsys *FileSystem[T]) StatSys(stat fs.FileInfo) *T {
return info.attr
}

func (fsys *FileSystem[T]) preSignGetUrl(s3key *string) (string, error) {
func (fsys *FileSystem[T]) preSignPutUrl(s3key *string, attr *T, ttl time.Duration) (string, error) {
req := &s3.PutObjectInput{
Bucket: aws.String(fsys.bucket),
Key: s3key,
Metadata: make(map[string]string),
}
fsys.codec.EncodePutInput(attr, req)

ctx, cancel := context.WithTimeout(context.Background(), fsys.timeout)
defer cancel()

val, err := fsys.signer.PresignPutObject(ctx, req, s3.WithPresignExpires(ttl))
if err != nil {
return "", &fs.PathError{
Op: "presign",
Path: "/" + aws.ToString(s3key),
Err: err,
}
}

return val.URL, nil
}

func (fsys *FileSystem[T]) preSignGetUrl(s3key *string, ttl time.Duration) (string, error) {
req := &s3.GetObjectInput{
Bucket: aws.String(fsys.bucket),
Key: s3key,
Expand All @@ -183,7 +207,7 @@ func (fsys *FileSystem[T]) preSignGetUrl(s3key *string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), fsys.timeout)
defer cancel()

val, err := fsys.signer.PresignGetObject(ctx, req, s3.WithPresignExpires(fsys.ttlSignedUrl))
val, err := fsys.signer.PresignGetObject(ctx, req, s3.WithPresignExpires(ttl))
if err != nil {
return "", &fs.PathError{
Op: "presign",
Expand Down Expand Up @@ -338,6 +362,38 @@ func (fsys *FileSystem[T]) Wait(path string, timeout time.Duration) error {
return nil
}

func (fsys *FileSystem[T]) PutFileUrl(path string, attr *T, ttl time.Duration) (string, error) {
if fsys.signer == nil {
return "", &fs.PathError{
Op: "putfileurl",
Path: path,
Err: errors.New("signer is not configured"),
}
}

if err := RequireValidPath("putfileurl", path); err != nil {
return "", err
}

return fsys.preSignPutUrl(s3Key(fsys.root, path), attr, ttl)
}

func (fsys *FileSystem[T]) GetFileUrl(path string, ttl time.Duration) (string, error) {
if fsys.signer == nil {
return "", &fs.PathError{
Op: "getfileurl",
Path: path,
Err: errors.New("signer is not configured"),
}
}

if err := RequireValidPath("getfileurl", path); err != nil {
return "", err
}

return fsys.preSignGetUrl(s3Key(fsys.root, path), ttl)
}

//------------------------------------------------------------------------------

func recoverNoSuchKey(err error) bool {
Expand Down
7 changes: 7 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ type CopyFS interface {
Wait(path string, timeout time.Duration) error
}

// File System extension supporting I/O operations via urls
type CurlFS[T any] interface {
fs.FS
PutFileUrl(path string, attr *T, ttl time.Duration) (string, error)
GetFileUrl(path string, ttl time.Duration) (string, error)
}

// well-known attributes controlled by S3 system
type SystemMetadata struct {
CacheControl string
Expand Down
Loading