2019-01-24 20:15:10 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
2018-11-26 07:39:05 +00:00
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package stream
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"io"
|
|
|
|
|
|
|
|
"storj.io/storj/pkg/storj"
|
2019-07-28 06:55:36 +01:00
|
|
|
"storj.io/storj/uplink/storage/streams"
|
2018-11-26 07:39:05 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Download implements Reader, Seeker and Closer for reading from stream.
|
|
|
|
type Download struct {
|
|
|
|
ctx context.Context
|
|
|
|
stream storj.ReadOnlyStream
|
|
|
|
streams streams.Store
|
|
|
|
reader io.ReadCloser
|
|
|
|
offset int64
|
2019-08-13 18:29:02 +01:00
|
|
|
limit int64
|
2018-11-26 07:39:05 +00:00
|
|
|
closed bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewDownload creates new stream download.
|
|
|
|
func NewDownload(ctx context.Context, stream storj.ReadOnlyStream, streams streams.Store) *Download {
|
|
|
|
return &Download{
|
|
|
|
ctx: ctx,
|
|
|
|
stream: stream,
|
|
|
|
streams: streams,
|
2019-08-13 18:29:02 +01:00
|
|
|
limit: -1,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewDownloadRange creates new stream range download with range from offset to offset+limit.
|
|
|
|
func NewDownloadRange(ctx context.Context, stream storj.ReadOnlyStream, streams streams.Store, offset, limit int64) *Download {
|
|
|
|
return &Download{
|
|
|
|
ctx: ctx,
|
|
|
|
stream: stream,
|
|
|
|
streams: streams,
|
|
|
|
offset: offset,
|
|
|
|
limit: limit,
|
2018-11-26 07:39:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read reads up to len(data) bytes into data.
|
|
|
|
//
|
|
|
|
// If this is the first call it will read from the beginning of the stream.
|
|
|
|
// Use Seek to change the current offset for the next Read call.
|
|
|
|
//
|
|
|
|
// See io.Reader for more details.
|
|
|
|
func (download *Download) Read(data []byte) (n int, err error) {
|
|
|
|
if download.closed {
|
|
|
|
return 0, Error.New("already closed")
|
|
|
|
}
|
|
|
|
|
|
|
|
if download.reader == nil {
|
2019-08-13 18:29:02 +01:00
|
|
|
err = download.resetReader(download.offset)
|
2018-11-26 07:39:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-13 18:29:02 +01:00
|
|
|
if download.limit == 0 {
|
|
|
|
return 0, io.EOF
|
2018-11-26 07:39:05 +00:00
|
|
|
}
|
2019-08-13 18:29:02 +01:00
|
|
|
if download.limit > 0 && download.limit < int64(len(data)) {
|
|
|
|
data = data[:download.limit]
|
2018-11-26 07:39:05 +00:00
|
|
|
}
|
2019-08-13 18:29:02 +01:00
|
|
|
n, err = download.reader.Read(data)
|
|
|
|
if download.limit >= 0 {
|
|
|
|
download.limit -= int64(n)
|
2018-11-26 07:39:05 +00:00
|
|
|
}
|
2019-08-13 18:29:02 +01:00
|
|
|
download.offset += int64(n)
|
2018-11-26 07:39:05 +00:00
|
|
|
|
2019-08-13 18:29:02 +01:00
|
|
|
return n, err
|
2018-11-26 07:39:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Close closes the stream and releases the underlying resources.
|
|
|
|
func (download *Download) Close() error {
|
|
|
|
if download.closed {
|
|
|
|
return Error.New("already closed")
|
|
|
|
}
|
|
|
|
|
|
|
|
download.closed = true
|
|
|
|
|
|
|
|
if download.reader == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return download.reader.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (download *Download) resetReader(offset int64) error {
|
|
|
|
if download.reader != nil {
|
|
|
|
err := download.reader.Close()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
obj := download.stream.Info()
|
|
|
|
|
2019-11-19 12:58:26 +00:00
|
|
|
rr, err := download.streams.Get(download.ctx, storj.JoinPaths(obj.Bucket.Name, obj.Path), obj, obj.Bucket.PathCipher)
|
2018-11-26 07:39:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
download.reader, err = rr.Range(download.ctx, offset, obj.Size-offset)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
download.offset = offset
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|