Files
RenewCMS/internal/infrastructure/storage/s3Reader.go
T

84 lines
1.6 KiB
Go

package storage
import (
"context"
"errors"
"fmt"
"io"
)
type rangeOpener func(offset int64) (io.ReadCloser, context.CancelFunc, error)
type s3Reader struct {
size int64
position int64
bodyOffset int64
body io.ReadCloser
cancel context.CancelFunc
reopen rangeOpener
}
func newS3Reader(size int64, body io.ReadCloser, cancel context.CancelFunc, reopen rangeOpener) *s3Reader {
return &s3Reader{size: size, body: body, cancel: cancel, reopen: reopen}
}
func (r *s3Reader) Read(p []byte) (int, error) {
if r.position >= r.size {
return 0, io.EOF
}
if r.body == nil || r.bodyOffset != r.position {
if err := r.openAtPosition(); err != nil {
return 0, err
}
}
n, err := r.body.Read(p)
r.position += int64(n)
r.bodyOffset = r.position
return n, err
}
func (r *s3Reader) openAtPosition() error {
r.release()
body, cancel, err := r.reopen(r.position)
if err != nil {
return err
}
r.body, r.cancel, r.bodyOffset = body, cancel, r.position
return nil
}
func (r *s3Reader) Seek(offset int64, whence int) (int64, error) {
var target int64
switch whence {
case io.SeekStart:
target = offset
case io.SeekCurrent:
target = r.position + offset
case io.SeekEnd:
target = r.size + offset
default:
return 0, errors.New("invalid seek whence")
}
if target < 0 {
return 0, fmt.Errorf("negative seek position %d", target)
}
r.position = target
return target, nil
}
func (r *s3Reader) Close() error {
r.release()
return nil
}
func (r *s3Reader) release() {
if r.body != nil {
_ = r.body.Close()
r.body = nil
}
if r.cancel != nil {
r.cancel()
r.cancel = nil
}
}