mirror of https://github.com/Xhofe/alist
110 lines
2.0 KiB
Go
110 lines
2.0 KiB
Go
package ftp
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/jlaffaye/ftp"
|
|
)
|
|
|
|
// do others that not defined in Driver interface
|
|
|
|
func (d *FTP) login() error {
|
|
if d.conn != nil {
|
|
_, err := d.conn.CurrentDir()
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
}
|
|
conn, err := ftp.Dial(d.Address, ftp.DialWithShutTimeout(10*time.Second))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = conn.Login(d.Username, d.Password)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
d.conn = conn
|
|
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
|
|
}
|