|
package ftp |
|
|
|
import ( |
|
"io" |
|
"os" |
|
"sync" |
|
"sync/atomic" |
|
"time" |
|
|
|
"github.com/jlaffaye/ftp" |
|
) |
|
|
|
|
|
|
|
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 |
|
} |
|
|
|
|
|
type FileReader struct { |
|
conn *ftp.ServerConn |
|
resp *ftp.Response |
|
offset atomic.Int64 |
|
readAtOffset int64 |
|
mu sync.Mutex |
|
path string |
|
size int64 |
|
} |
|
|
|
func NewFileReader(conn *ftp.ServerConn, path string, size int64) *FileReader { |
|
return &FileReader{ |
|
conn: conn, |
|
path: path, |
|
size: size, |
|
} |
|
} |
|
|
|
func (r *FileReader) Read(buf []byte) (n int, err error) { |
|
n, err = r.ReadAt(buf, r.offset.Load()) |
|
r.offset.Add(int64(n)) |
|
return |
|
} |
|
|
|
func (r *FileReader) ReadAt(buf []byte, off int64) (n int, err error) { |
|
if off < 0 { |
|
return -1, os.ErrInvalid |
|
} |
|
r.mu.Lock() |
|
defer r.mu.Unlock() |
|
|
|
if off != r.readAtOffset { |
|
|
|
_ = r.resp.Close() |
|
r.resp = nil |
|
} |
|
|
|
if r.resp == nil { |
|
r.resp, err = r.conn.RetrFrom(r.path, uint64(off)) |
|
r.readAtOffset = off |
|
if err != nil { |
|
return 0, err |
|
} |
|
} |
|
|
|
n, err = r.resp.Read(buf) |
|
r.readAtOffset += int64(n) |
|
return |
|
} |
|
|
|
func (r *FileReader) Seek(offset int64, whence int) (int64, error) { |
|
oldOffset := r.offset.Load() |
|
var newOffset int64 |
|
switch whence { |
|
case io.SeekStart: |
|
newOffset = offset |
|
case io.SeekCurrent: |
|
newOffset = oldOffset + offset |
|
case io.SeekEnd: |
|
return r.size, nil |
|
default: |
|
return -1, os.ErrInvalid |
|
} |
|
|
|
if newOffset < 0 { |
|
|
|
return oldOffset, os.ErrInvalid |
|
} |
|
if newOffset == oldOffset { |
|
|
|
return oldOffset, nil |
|
} |
|
r.offset.Store(newOffset) |
|
return newOffset, nil |
|
} |
|
|
|
func (r *FileReader) Close() error { |
|
if r.resp != nil { |
|
return r.resp.Close() |
|
} |
|
return nil |
|
} |
|
|