Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/drivers/ftp/util.go
1987 views
1
package ftp
2
3
import (
4
"io"
5
"os"
6
"sync"
7
"sync/atomic"
8
"time"
9
10
"github.com/jlaffaye/ftp"
11
)
12
13
// do others that not defined in Driver interface
14
15
func (d *FTP) login() error {
16
if d.conn != nil {
17
_, err := d.conn.CurrentDir()
18
if err == nil {
19
return nil
20
}
21
}
22
conn, err := ftp.Dial(d.Address, ftp.DialWithShutTimeout(10*time.Second))
23
if err != nil {
24
return err
25
}
26
err = conn.Login(d.Username, d.Password)
27
if err != nil {
28
return err
29
}
30
d.conn = conn
31
return nil
32
}
33
34
// FileReader An FTP file reader that implements io.MFile for seeking.
35
type FileReader struct {
36
conn *ftp.ServerConn
37
resp *ftp.Response
38
offset atomic.Int64
39
readAtOffset int64
40
mu sync.Mutex
41
path string
42
size int64
43
}
44
45
func NewFileReader(conn *ftp.ServerConn, path string, size int64) *FileReader {
46
return &FileReader{
47
conn: conn,
48
path: path,
49
size: size,
50
}
51
}
52
53
func (r *FileReader) Read(buf []byte) (n int, err error) {
54
n, err = r.ReadAt(buf, r.offset.Load())
55
r.offset.Add(int64(n))
56
return
57
}
58
59
func (r *FileReader) ReadAt(buf []byte, off int64) (n int, err error) {
60
if off < 0 {
61
return -1, os.ErrInvalid
62
}
63
r.mu.Lock()
64
defer r.mu.Unlock()
65
66
if off != r.readAtOffset {
67
//have to restart the connection, to correct offset
68
_ = r.resp.Close()
69
r.resp = nil
70
}
71
72
if r.resp == nil {
73
r.resp, err = r.conn.RetrFrom(r.path, uint64(off))
74
r.readAtOffset = off
75
if err != nil {
76
return 0, err
77
}
78
}
79
80
n, err = r.resp.Read(buf)
81
r.readAtOffset += int64(n)
82
return
83
}
84
85
func (r *FileReader) Seek(offset int64, whence int) (int64, error) {
86
oldOffset := r.offset.Load()
87
var newOffset int64
88
switch whence {
89
case io.SeekStart:
90
newOffset = offset
91
case io.SeekCurrent:
92
newOffset = oldOffset + offset
93
case io.SeekEnd:
94
return r.size, nil
95
default:
96
return -1, os.ErrInvalid
97
}
98
99
if newOffset < 0 {
100
// offset out of range
101
return oldOffset, os.ErrInvalid
102
}
103
if newOffset == oldOffset {
104
// offset not changed, so return directly
105
return oldOffset, nil
106
}
107
r.offset.Store(newOffset)
108
return newOffset, nil
109
}
110
111
func (r *FileReader) Close() error {
112
if r.resp != nil {
113
return r.resp.Close()
114
}
115
return nil
116
}
117
118