From 25b4b55ee108576217259bd850781f5a5f4fc3ce Mon Sep 17 00:00:00 2001 From: KirCute_ECT <951206789@qq.com> Date: Fri, 10 Jan 2025 20:50:20 +0800 Subject: [PATCH] feat(ftp-server): support resumable downloading (#7792) --- go.mod | 4 ++-- go.sum | 8 ++++---- server/ftp/afero.go | 8 ++++---- server/ftp/fsread.go | 47 ++++++++++++++++++++++++++++++++++++++------ server/ftp/fsup.go | 2 +- 5 files changed, 52 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index 1deaa1d5..7ca66e15 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.22.4 require ( github.com/KirCute/ftpserverlib-pasvportmap v1.25.0 - github.com/KirCute/sftpd-alist v0.0.11 + github.com/KirCute/sftpd-alist v0.0.12 github.com/SheltonZhu/115driver v1.0.32 github.com/Xhofe/go-cache v0.0.0-20240804043513-b1a71927bc21 github.com/Xhofe/rateg v0.0.0-20230728072201-251a4e1adad4 @@ -62,7 +62,7 @@ require ( github.com/xhofe/tache v0.1.3 github.com/xhofe/wopan-sdk-go v0.1.3 github.com/zzzhr1990/go-common-entity v0.0.0-20221216044934-fd1c571e3a22 - golang.org/x/crypto v0.30.0 + golang.org/x/crypto v0.31.0 golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e golang.org/x/image v0.19.0 golang.org/x/net v0.28.0 diff --git a/go.sum b/go.sum index a4e8e12d..101a0bea 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/KirCute/ftpserverlib-pasvportmap v1.25.0 h1:ikwCzeqoqN6wvBHOB9OI6dde/jbV7EoTMpUcxtYl5Po= github.com/KirCute/ftpserverlib-pasvportmap v1.25.0/go.mod h1:v0NgMtKDDi/6CM6r4P+daCljCW3eO9yS+Z+pZDTKo1E= -github.com/KirCute/sftpd-alist v0.0.11 h1:BGInXmmLBI+v6S9WZCwvY0DRK1vDprGNcTv/57p2GSo= -github.com/KirCute/sftpd-alist v0.0.11/go.mod h1:pPFzr6GrKqXvFXLr46ZpoqmtSpwH8DKTYloSp/ybzKQ= +github.com/KirCute/sftpd-alist v0.0.12 h1:GNVM5QLbQLAfXP4wGUlXFA2IO6fVek0n0IsGnOuISdg= +github.com/KirCute/sftpd-alist v0.0.12/go.mod h1:2wNK7yyW2XfjyJq10OY6xB4COLac64hOwfV6clDJn6s= github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd h1:nzE1YQBdx1bq9IlZinHa+HVffy+NmVRoKr+wHN8fpLE= github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd/go.mod h1:C8yoIfvESpM3GD07OCHU7fqI7lhwyZ2Td1rbNbTAhnc= github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4S2OByM= @@ -574,8 +574,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= -golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= -golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk= golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= diff --git a/server/ftp/afero.go b/server/ftp/afero.go index 448744b1..75ae2e43 100644 --- a/server/ftp/afero.go +++ b/server/ftp/afero.go @@ -83,9 +83,6 @@ func (a *AferoAdapter) ReadDir(name string) ([]os.FileInfo, error) { func (a *AferoAdapter) GetHandle(name string, flags int, offset int64) (ftpserver.FileTransfer, error) { fileSize := a.nextFileSize a.nextFileSize = 0 - if offset != 0 { - return nil, errs.NotSupport - } if (flags & os.O_SYNC) != 0 { return nil, errs.NotSupport } @@ -106,6 +103,9 @@ func (a *AferoAdapter) GetHandle(name string, flags int, offset int64) (ftpserve return nil, errors.New("file already exists") } if (flags & os.O_WRONLY) != 0 { + if offset != 0 { + return nil, errs.NotSupport + } trunc := (flags & os.O_TRUNC) != 0 if fileSize > 0 { return OpenUploadWithLength(a.ctx, path, trunc, fileSize) @@ -113,7 +113,7 @@ func (a *AferoAdapter) GetHandle(name string, flags int, offset int64) (ftpserve return OpenUpload(a.ctx, path, trunc) } } - return OpenDownload(a.ctx, path) + return OpenDownload(a.ctx, path, offset) } func (a *AferoAdapter) SetNextFileSize(size int64) { diff --git a/server/ftp/fsread.go b/server/ftp/fsread.go index 74d184b6..257d2ec8 100644 --- a/server/ftp/fsread.go +++ b/server/ftp/fsread.go @@ -8,6 +8,7 @@ import ( "github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/op" "github.com/alist-org/alist/v3/internal/stream" + "github.com/alist-org/alist/v3/pkg/http_range" "github.com/alist-org/alist/v3/server/common" "github.com/pkg/errors" "io" @@ -19,10 +20,12 @@ import ( type FileDownloadProxy struct { ftpserver.FileTransfer - reader io.ReadCloser + ss *stream.SeekableStream + reader io.Reader + cur int64 } -func OpenDownload(ctx context.Context, reqPath string) (*FileDownloadProxy, error) { +func OpenDownload(ctx context.Context, reqPath string, offset int64) (*FileDownloadProxy, error) { user := ctx.Value("user").(*model.User) meta, err := op.GetNearestMeta(reqPath) if err != nil { @@ -52,11 +55,22 @@ func OpenDownload(ctx context.Context, reqPath string) (*FileDownloadProxy, erro if err != nil { return nil, err } - return &FileDownloadProxy{reader: ss}, nil + var reader io.Reader + if offset != 0 { + reader, err = ss.RangeRead(http_range.Range{Start: offset, Length: -1}) + if err != nil { + return nil, err + } + } else { + reader = ss + } + return &FileDownloadProxy{ss: ss, reader: reader}, nil } func (f *FileDownloadProxy) Read(p []byte) (n int, err error) { - return f.reader.Read(p) + n, err = f.reader.Read(p) + f.cur += int64(n) + return n, err } func (f *FileDownloadProxy) Write(p []byte) (n int, err error) { @@ -64,11 +78,32 @@ func (f *FileDownloadProxy) Write(p []byte) (n int, err error) { } func (f *FileDownloadProxy) Seek(offset int64, whence int) (int64, error) { - return 0, errs.NotSupport + switch whence { + case io.SeekStart: + break + case io.SeekCurrent: + offset += f.cur + break + case io.SeekEnd: + offset += f.ss.GetSize() + break + default: + return 0, errs.NotSupport + } + if offset < 0 { + return 0, errors.New("Seek: negative position") + } + reader, err := f.ss.RangeRead(http_range.Range{Start: offset, Length: -1}) + if err != nil { + return f.cur, err + } + f.cur = offset + f.reader = reader + return offset, nil } func (f *FileDownloadProxy) Close() error { - return f.reader.Close() + return f.ss.Close() } type OsFileInfoAdapter struct { diff --git a/server/ftp/fsup.go b/server/ftp/fsup.go index 96c84681..4d626d0e 100644 --- a/server/ftp/fsup.go +++ b/server/ftp/fsup.go @@ -63,7 +63,7 @@ func (f *FileUploadProxy) Write(p []byte) (n int, err error) { } func (f *FileUploadProxy) Seek(offset int64, whence int) (int64, error) { - return 0, errs.NotSupport + return f.buffer.Seek(offset, whence) } func (f *FileUploadProxy) Close() error {