2022-09-03 22:07:08 +08:00
|
|
|
package ftp
|
|
|
|
|
2023-03-11 21:02:47 +08:00
|
|
|
import (
|
2025-08-06 13:32:37 +08:00
|
|
|
"context"
|
|
|
|
"errors"
|
2025-07-12 17:57:54 +08:00
|
|
|
"fmt"
|
2023-03-11 21:02:47 +08:00
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
2025-07-12 17:57:54 +08:00
|
|
|
"github.com/OpenListTeam/OpenList/v4/pkg/singleflight"
|
2025-08-06 13:32:37 +08:00
|
|
|
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
2023-03-11 21:02:47 +08:00
|
|
|
"github.com/jlaffaye/ftp"
|
|
|
|
)
|
2022-09-03 22:07:08 +08:00
|
|
|
|
|
|
|
// do others that not defined in Driver interface
|
|
|
|
|
|
|
|
func (d *FTP) login() error {
|
2025-08-05 21:42:54 +08:00
|
|
|
_, err, _ := singleflight.AnyGroup.Do(fmt.Sprintf("FTP.login:%p", d), func() (any, error) {
|
2025-08-06 13:32:37 +08:00
|
|
|
var err error
|
|
|
|
if d.conn != nil {
|
|
|
|
err = d.conn.NoOp()
|
|
|
|
if err != nil {
|
|
|
|
d.conn.Quit()
|
|
|
|
d.conn = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if d.conn == nil {
|
|
|
|
d.conn, err = d._login(d.ctx)
|
|
|
|
}
|
|
|
|
return nil, err
|
2025-07-12 17:57:54 +08:00
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2025-08-06 13:32:37 +08:00
|
|
|
func (d *FTP) _login(ctx context.Context) (*ftp.ServerConn, error) {
|
|
|
|
conn, err := ftp.Dial(d.Address, ftp.DialWithShutTimeout(10*time.Second), ftp.DialWithContext(ctx))
|
2022-09-03 22:07:08 +08:00
|
|
|
if err != nil {
|
2025-08-06 13:32:37 +08:00
|
|
|
return nil, err
|
2022-09-03 22:07:08 +08:00
|
|
|
}
|
|
|
|
err = conn.Login(d.Username, d.Password)
|
|
|
|
if err != nil {
|
2025-08-06 13:32:37 +08:00
|
|
|
conn.Quit()
|
|
|
|
return nil, err
|
2022-09-03 22:07:08 +08:00
|
|
|
}
|
2025-08-06 13:32:37 +08:00
|
|
|
return conn, nil
|
2022-09-03 22:07:08 +08:00
|
|
|
}
|
2023-03-11 21:02:47 +08:00
|
|
|
|
2023-09-03 18:47:32 +08:00
|
|
|
type FileReader struct {
|
2025-08-06 13:32:37 +08:00
|
|
|
*ftp.Response
|
|
|
|
io.Reader
|
|
|
|
ctx context.Context
|
2023-03-11 21:02:47 +08:00
|
|
|
}
|
|
|
|
|
2025-08-06 13:32:37 +08:00
|
|
|
func (r *FileReader) Read(buf []byte) (int, error) {
|
|
|
|
n := 0
|
|
|
|
for n < len(buf) {
|
|
|
|
w, err := r.Reader.Read(buf[n:])
|
|
|
|
if utils.IsCanceled(r.ctx) {
|
|
|
|
return n, r.ctx.Err()
|
|
|
|
}
|
|
|
|
n += w
|
|
|
|
if errors.Is(err, os.ErrDeadlineExceeded) {
|
|
|
|
r.Response.SetDeadline(time.Now().Add(time.Second))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err != nil || w == 0 {
|
|
|
|
return n, err
|
2023-03-11 21:02:47 +08:00
|
|
|
}
|
|
|
|
}
|
2025-08-06 13:32:37 +08:00
|
|
|
return n, nil
|
2023-03-11 21:02:47 +08:00
|
|
|
}
|