package utils import ( "fmt" "io" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/s3" ) type S3ReadSeeker struct { client *s3.S3 bucket string key string offset int64 body io.ReadCloser contentLen int64 } func NewS3ReadSeeker(client *s3.S3, bucket, key string, body io.ReadCloser, contentLen int64) *S3ReadSeeker { return &S3ReadSeeker{ client: client, bucket: bucket, key: key, body: body, contentLen: contentLen, } } func (r *S3ReadSeeker) Read(p []byte) (n int, err error) { if r.body == nil { return 0, io.EOF } return r.body.Read(p) } func (r *S3ReadSeeker) Seek(offset int64, whence int) (int64, error) { if r.body != nil { r.body.Close() r.body = nil } var newOffset int64 switch whence { case io.SeekStart: newOffset = offset case io.SeekCurrent: newOffset = r.offset + offset case io.SeekEnd: newOffset = r.contentLen + offset default: return 0, fmt.Errorf("invalid whence") } if newOffset < 0 { return 0, fmt.Errorf("negative offset") } if newOffset >= r.contentLen { return r.contentLen, nil } result, err := r.client.GetObject(&s3.GetObjectInput{ Bucket: aws.String(r.bucket), Key: aws.String(r.key), Range: aws.String(fmt.Sprintf("bytes=%d-", newOffset)), }) if err != nil { return 0, err } r.body = result.Body r.offset = newOffset return newOffset, nil } func (r *S3ReadSeeker) Close() error { if r.body != nil { return r.body.Close() } return nil }