feat(ftp): support seek/range request (#3811)
This commit is contained in:
parent
666e02f0c3
commit
4d9a29bddd
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
stdpath "path"
|
stdpath "path"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
@ -44,8 +45,7 @@ func (d *FTP) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]m
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
res := make([]model.Obj, 0)
|
res := make([]model.Obj, 0)
|
||||||
for i, _ := range entries {
|
for _, entry := range entries {
|
||||||
entry := entries[i]
|
|
||||||
if entry.Name == "." || entry.Name == ".." {
|
if entry.Name == "." || entry.Name == ".." {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -64,13 +64,13 @@ func (d *FTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*m
|
|||||||
if err := d.login(); err != nil {
|
if err := d.login(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
resp, err := d.conn.Retr(file.GetPath())
|
|
||||||
if err != nil {
|
r := NewFTPFileReader(d.conn, file.GetPath())
|
||||||
return nil, err
|
link := &model.Link{
|
||||||
|
Data: r,
|
||||||
}
|
}
|
||||||
return &model.Link{
|
base.HandleRange(link, r, args.Header, file.GetSize())
|
||||||
Data: resp,
|
return link, nil
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *FTP) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
func (d *FTP) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
package ftp
|
package ftp
|
||||||
|
|
||||||
import "github.com/jlaffaye/ftp"
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jlaffaye/ftp"
|
||||||
|
)
|
||||||
|
|
||||||
// do others that not defined in Driver interface
|
// do others that not defined in Driver interface
|
||||||
|
|
||||||
@ -11,7 +18,7 @@ func (d *FTP) login() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
conn, err := ftp.Dial(d.Address)
|
conn, err := ftp.Dial(d.Address, ftp.DialWithShutTimeout(10*time.Second))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -22,3 +29,81 @@ func (d *FTP) login() error {
|
|||||||
d.conn = conn
|
d.conn = conn
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An FTP file reader that implements io.ReadSeekCloser for seeking.
|
||||||
|
type FTPFileReader struct {
|
||||||
|
conn *ftp.ServerConn
|
||||||
|
resp *ftp.Response
|
||||||
|
offset int64
|
||||||
|
mu sync.Mutex
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFTPFileReader(conn *ftp.ServerConn, path string) *FTPFileReader {
|
||||||
|
return &FTPFileReader{
|
||||||
|
conn: conn,
|
||||||
|
path: path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FTPFileReader) Read(buf []byte) (n int, err error) {
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
|
||||||
|
if r.resp == nil {
|
||||||
|
r.resp, err = r.conn.RetrFrom(r.path, uint64(r.offset))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err = r.resp.Read(buf)
|
||||||
|
r.offset += int64(n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FTPFileReader) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
|
||||||
|
oldOffset := r.offset
|
||||||
|
var newOffset int64
|
||||||
|
switch whence {
|
||||||
|
case io.SeekStart:
|
||||||
|
newOffset = offset
|
||||||
|
case io.SeekCurrent:
|
||||||
|
newOffset = oldOffset + offset
|
||||||
|
case io.SeekEnd:
|
||||||
|
size, err := r.conn.FileSize(r.path)
|
||||||
|
if err != nil {
|
||||||
|
return oldOffset, err
|
||||||
|
}
|
||||||
|
newOffset = offset + int64(size)
|
||||||
|
default:
|
||||||
|
return -1, os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
if newOffset < 0 {
|
||||||
|
// offset out of range
|
||||||
|
return oldOffset, os.ErrInvalid
|
||||||
|
}
|
||||||
|
if newOffset == oldOffset {
|
||||||
|
// offset not changed, so return directly
|
||||||
|
return oldOffset, nil
|
||||||
|
}
|
||||||
|
r.offset = newOffset
|
||||||
|
|
||||||
|
if r.resp != nil {
|
||||||
|
// close the existing ftp data connection, otherwise the next read will be blocked
|
||||||
|
_ = r.resp.Close() // we do not care about whether it returns an error
|
||||||
|
r.resp = nil
|
||||||
|
}
|
||||||
|
return newOffset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FTPFileReader) Close() error {
|
||||||
|
if r.resp != nil {
|
||||||
|
return r.resp.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user