Skip to content

Commit 9c9ac80

Browse files
authored
add range download method (#1769)
1 parent b903c80 commit 9c9ac80

File tree

2 files changed

+112
-1
lines changed

2 files changed

+112
-1
lines changed

core/sys/util.go

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,10 +290,18 @@ func (pf *PipeFile) Close() error {
290290
return pf.w.Close()
291291
}
292292

293+
func (pf *PipeFile) CloseWithError(err error) {
294+
pf.w.CloseWithError(err)
295+
}
296+
293297
func (pf *PipeFile) Read(p []byte) (int, error) {
294298
return pf.r.Read(p)
295299
}
296300

301+
func (pf *PipeFile) Reader() io.ReadCloser {
302+
return pf.r
303+
}
304+
297305
func (pf *PipeFile) Stat() (fs.FileInfo, error) {
298306
return nil, nil
299307
}
@@ -303,5 +311,39 @@ func (pf *PipeFile) Sync() error {
303311
}
304312

305313
func (pf *PipeFile) Seek(offset int64, whence int) (int64, error) {
306-
return 0, nil
314+
if whence != io.SeekStart {
315+
return 0, os.ErrInvalid
316+
}
317+
if offset < 0 {
318+
return 0, os.ErrInvalid
319+
}
320+
return io.CopyN(io.Discard, pf.r, offset)
321+
}
322+
323+
type LimitedReaderCloser struct {
324+
R io.ReadCloser // underlying reader
325+
N int64 // max bytes remaining
326+
}
327+
328+
func NewLimitedReaderCloser(r io.ReadCloser, n int64) *LimitedReaderCloser {
329+
return &LimitedReaderCloser{
330+
R: r,
331+
N: n,
332+
}
333+
}
334+
335+
func (l *LimitedReaderCloser) Read(p []byte) (n int, err error) {
336+
if l.N <= 0 {
337+
return 0, io.EOF
338+
}
339+
if int64(len(p)) > l.N {
340+
p = p[0:l.N]
341+
}
342+
n, err = l.R.Read(p)
343+
l.N -= int64(n)
344+
return
345+
}
346+
347+
func (l *LimitedReaderCloser) Close() error {
348+
return l.R.Close()
307349
}

zboxcore/sdk/allocation.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3405,6 +3405,75 @@ func (a *Allocation) DownloadDirectory(ctx context.Context, remotePath, localPat
34053405
return nil
34063406
}
34073407

3408+
func (a *Allocation) DownloadObject(ctx context.Context, remotePath string, rangeStart, rangeEnd int64) (io.ReadCloser, error) {
3409+
fm, err := a.GetFileMeta(remotePath)
3410+
if err != nil {
3411+
return nil, err
3412+
}
3413+
if fm.Type != fileref.FILE {
3414+
return nil, errors.New("invalid_file_type", "Invalid file type. Expected file type")
3415+
}
3416+
var (
3417+
startBlock, endBlock int64
3418+
)
3419+
effectiveChunkSize := a.GetChunkReadSize(fm.EncryptedKey != "")
3420+
fileRangeSize := rangeEnd - rangeStart + 1
3421+
if rangeEnd < rangeStart {
3422+
fileRangeSize = fm.ActualFileSize
3423+
}
3424+
if rangeEnd >= rangeStart {
3425+
startBlock = int64(rangeStart / effectiveChunkSize)
3426+
if startBlock == 0 {
3427+
startBlock = 1
3428+
}
3429+
if rangeEnd < fileRangeSize {
3430+
endBlock = (fileRangeSize + effectiveChunkSize - 1) / effectiveChunkSize
3431+
} else {
3432+
endBlock = int64(rangeEnd+effectiveChunkSize-1) / effectiveChunkSize
3433+
}
3434+
} else {
3435+
startBlock = 1
3436+
endBlock = 0
3437+
}
3438+
3439+
if rangeEnd == -1 {
3440+
endBlock = 0
3441+
startBlock = int64(rangeStart / effectiveChunkSize)
3442+
if startBlock == 0 {
3443+
startBlock = 1
3444+
}
3445+
fileRangeSize = fm.ActualFileSize - rangeStart
3446+
}
3447+
pipeFile := sys.NewPipeFile()
3448+
wg := &sync.WaitGroup{}
3449+
wg.Add(1)
3450+
downloadStatusBar := &StatusBar{
3451+
wg: wg,
3452+
}
3453+
err = a.DownloadByBlocksToFileHandler(pipeFile, remotePath, startBlock, endBlock, numBlockDownloads, false, downloadStatusBar, true)
3454+
if err != nil {
3455+
return nil, err
3456+
}
3457+
go func() {
3458+
wg.Wait()
3459+
if downloadStatusBar.err != nil {
3460+
pipeFile.CloseWithError(downloadStatusBar.err) //nolint: errcheck
3461+
} else {
3462+
pipeFile.Close() //nolint: errcheck
3463+
}
3464+
}()
3465+
startOffset := rangeStart - (startBlock-1)*effectiveChunkSize
3466+
3467+
if startOffset > 0 {
3468+
_, err = pipeFile.Seek(startOffset, io.SeekStart)
3469+
if err != nil {
3470+
return nil, err
3471+
}
3472+
}
3473+
lr := sys.NewLimitedReaderCloser(pipeFile.Reader(), fileRangeSize)
3474+
return lr, nil
3475+
}
3476+
34083477
// contextCanceled returns whether a context is canceled.
34093478
func contextCanceled(ctx context.Context) bool {
34103479
select {

0 commit comments

Comments
 (0)