mirror of https://github.com/Xhofe/alist
feat(ftp-server): support resumable downloading (#7792)
parent
6812ec9a6d
commit
25b4b55ee1
4
go.mod
4
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
|
||||
|
|
8
go.sum
8
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=
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue