ftpclient

package
v1.19.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 4, 2026 License: MIT Imports: 10 Imported by: 0

README

FTPClient Package

Go Reference

Thread-safe FTP client with automatic reconnection, TLS support, and comprehensive error handling for Go applications.

Table of Contents

Overview

The ftpclient package provides a production-ready FTP client implementation built on top of github.com/jlaffaye/ftp. It adds critical features for enterprise applications including thread safety, automatic connection management, and flexible configuration.

Why FTPClient?
  • Thread-Safe: Concurrent operations using atomic values and mutexes
  • Resilient: Automatic reconnection on connection failures
  • Flexible: Extensive configuration options including TLS, timeouts, and protocol features
  • Observable: Comprehensive error handling with custom error codes
  • Production-Ready: Context support for cancellation and deadlines

Features

Core Capabilities
  • Connection Management

    • Automatic connection pooling
    • Health checks with NOOP commands
    • Graceful reconnection on failures
    • Context-aware timeouts
  • Security

    • TLS/SSL support (explicit and implicit)
    • Custom TLS configuration via certificates package
    • Secure credential handling
  • File Operations

    • Upload/Download with offset support
    • Append to existing files
    • Recursive directory operations
    • File metadata (size, modification time)
  • Directory Operations

    • List (MLSD/LIST) and NameList (NLST)
    • Create and remove directories
    • Recursive directory removal
    • Directory tree walking
  • Protocol Features

    • UTF-8 support
    • Extended Passive Mode (EPSV) - RFC 2428
    • Machine Listings (MLSD) - RFC 3659
    • File Modification Time (MDTM/MFMT) - RFC 3659
    • Timezone support

Installation

go get github.com/nabbar/golib/ftpclient
Dependencies
github.com/jlaffaye/ftp                   # Core FTP implementation
github.com/nabbar/golib/certificates      # TLS configuration
github.com/nabbar/golib/errors            # Error handling
github.com/go-playground/validator/v10    # Configuration validation

Architecture

Component Diagram
┌──────────────────────────────────────────────────────┐
│                   Application                        │
└──────────────────┬───────────────────────────────────┘
                   │
                   ↓
┌──────────────────────────────────────────────────────┐
│              FTPClient Interface                     │
│  (Public API - Thread-Safe Operations)              │
└──────────────────┬───────────────────────────────────┘
                   │
                   ↓
┌──────────────────────────────────────────────────────┐
│               ftpClient (struct)                     │
│  • sync.Mutex for thread safety                     │
│  • atomic.Value for config/connection                │
│  • Automatic health checks                           │
└──────────────────┬───────────────────────────────────┘
                   │
                   ↓
┌──────────────────────────────────────────────────────┐
│            Config (Configuration)                    │
│  • Validation rules                                  │
│  • TLS settings                                      │
│  • Protocol options                                  │
└──────────────────┬───────────────────────────────────┘
                   │
                   ↓
┌──────────────────────────────────────────────────────┐
│        github.com/jlaffaye/ftp.ServerConn            │
│        (Underlying FTP Connection)                   │
└──────────────────────────────────────────────────────┘
Thread Safety Model

The package uses a combination of mutexes and atomic values to ensure thread safety:

┌─────────────────┐
│  Mutex Lock     │ ← Protects config/connection access
└────────┬────────┘
         │
         ↓
┌─────────────────┐
│  atomic.Value   │ ← Stores config and connection
│  • cfg          │
│  • cli          │
└────────┬────────┘
         │
         ↓
┌─────────────────┐
│  NOOP Check     │ ← Validates connection health
└─────────────────┘

Quick Start

Basic Usage
package main

import (
    "context"
    "log"
    "os"
    "time"

    "github.com/nabbar/golib/ftpclient"
)

func main() {
    // Create configuration
    cfg := &ftpclient.Config{
        Hostname:    "ftp.example.com:21",
        Login:       "username",
        Password:    "password",
        ConnTimeout: 30 * time.Second,
    }

    // Register context provider
    cfg.RegisterContext(func() context.Context {
        return context.Background()
    })

    // Create client
    client, err := ftpclient.New(cfg)
    if err != nil {
        log.Fatal("Failed to create client:", err)
    }
    defer client.Close()

    // Upload a file
    file, err := os.Open("local.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    if err := client.Stor("remote.txt", file); err != nil {
        log.Fatal("Upload failed:", err)
    }

    log.Println("File uploaded successfully")
}
With TLS
cfg := &ftpclient.Config{
    Hostname:    "ftps.example.com:990",
    Login:       "username",
    Password:    "password",
    ConnTimeout: 30 * time.Second,
    ForceTLS:    true, // Explicit TLS
}

// Provide TLS configuration
cfg.RegisterDefaultTLS(func() libtls.TLSConfig {
    // Return your TLS config here
    return nil // Uses default if nil
})

client, err := ftpclient.New(cfg)
// ... use client
Download with Progress
// Download file
resp, err := client.Retr("largefile.bin")
if err != nil {
    log.Fatal(err)
}
defer resp.Close()

// Create destination
out, err := os.Create("local-largefile.bin")
if err != nil {
    log.Fatal(err)
}
defer out.Close()

// Copy with progress tracking
written, err := io.Copy(out, resp)
if err != nil {
    log.Fatal(err)
}

log.Printf("Downloaded %d bytes\n", written)

Configuration

Config Structure
type Config struct {
    // Connection Settings
    Hostname    string        // Server address (required, RFC1123)
    Login       string        // Username (optional for anonymous)
    Password    string        // Password
    ConnTimeout time.Duration // Global timeout for operations

    // Timezone Settings
    TimeZone ConfigTimeZone   // Force specific timezone

    // Protocol Options
    DisableUTF8 bool          // Disable UTF-8 support
    DisableEPSV bool          // Disable Extended Passive Mode
    DisableMLSD bool          // Disable Machine Listings
    EnableMDTM  bool          // Enable MDTM write support

    // Security
    ForceTLS bool             // Require explicit TLS
    TLS      libtls.Config    // TLS configuration
}
Configuration Examples
Anonymous FTP
cfg := &ftpclient.Config{
    Hostname: "ftp.example.com:21",
    // Login and Password omitted for anonymous access
}
With Timezone
cfg := &ftpclient.Config{
    Hostname: "ftp.example.com:21",
    Login:    "user",
    Password: "pass",
    TimeZone: ftpclient.ConfigTimeZone{
        Name:   "America/New_York",
        Offset: -5 * 3600, // -5 hours in seconds
    },
}
Advanced Options
cfg := &ftpclient.Config{
    Hostname:    "ftp.example.com:21",
    Login:       "user",
    Password:    "pass",
    ConnTimeout: 60 * time.Second,
    DisableEPSV: true,  // Some old servers need this
    EnableMDTM:  true,  // For VsFTPd compatibility
    ForceTLS:    true,
}
Validation

The configuration is automatically validated using struct tags:

cfg := &ftpclient.Config{
    Hostname: "invalid!hostname",
}

if err := cfg.Validate(); err != nil {
    // Handle validation errors
    log.Fatal(err)
}

Operations

File Operations
Upload (STOR)
// Simple upload
file, _ := os.Open("document.pdf")
defer file.Close()
err := client.Stor("uploads/document.pdf", file)

// Upload with offset (resume)
err = client.StorFrom("uploads/large.bin", file, 1024*1024)

// Append to file
err = client.Append("logs/app.log", strings.NewReader("New log entry\n"))
Download (RETR)
// Download file
resp, err := client.Retr("data.csv")
if err != nil {
    log.Fatal(err)
}
defer resp.Close()

// Save to local file
out, _ := os.Create("local-data.csv")
defer out.Close()
io.Copy(out, resp)

// Download from offset (resume)
resp, err = client.RetrFrom("large.bin", 1024*1024)
File Metadata
// Get file size
size, err := client.FileSize("document.pdf")
fmt.Printf("File size: %d bytes\n", size)

// Get modification time
modTime, err := client.GetTime("document.pdf")
fmt.Printf("Last modified: %v\n", modTime)

// Set modification time
err = client.SetTime("document.pdf", time.Now().Add(-24*time.Hour))
File Management
// Rename file
err := client.Rename("old-name.txt", "new-name.txt")

// Delete file
err = client.Delete("temporary.tmp")
Directory Operations
Listing
// List with details (MLSD/LIST)
entries, err := client.List("/uploads")
for _, entry := range entries {
    fmt.Printf("%s - %d bytes - %v\n", 
        entry.Name, entry.Size, entry.Time)
}

// Simple name list (NLST)
names, err := client.NameList("/uploads")
for _, name := range names {
    fmt.Println(name)
}
Navigation
// Change directory
err := client.ChangeDir("/uploads/2024")

// Get current directory
dir, err := client.CurrentDir()
fmt.Printf("Current directory: %s\n", dir)
Directory Management
// Create directory
err := client.MakeDir("/backups/2024")

// Remove empty directory
err = client.RemoveDir("/temp")

// Remove directory recursively
err = client.RemoveDirRecur("/old-data")
Directory Walking
walker, err := client.Walk("/data")
if err != nil {
    log.Fatal(err)
}

for walker.Next() {
    entry := walker.Stat()
    path := walker.Path()
    fmt.Printf("%s: %s (%d bytes)\n", 
        path, entry.Name(), entry.Size())
}

if err := walker.Err(); err != nil {
    log.Fatal(err)
}
Connection Management
// Explicit connect (optional, done automatically)
err := client.Connect()

// Check connection health
err = client.Check()

// Close connection
client.Close()

Error Handling

Error Codes

The package defines custom error codes for precise error handling:

const (
    ErrorParamsEmpty        // Given parameters are empty
    ErrorValidatorError     // Configuration validation failed
    ErrorEndpointParser     // Cannot parse endpoint
    ErrorNotInitialized     // Client not initialized
    ErrorFTPConnection      // Connection failed
    ErrorFTPConnectionCheck // Health check (NOOP) failed
    ErrorFTPLogin          // Authentication failed
    ErrorFTPCommand        // Command execution failed
)
Error Handling Example
client, err := ftpclient.New(cfg)
if err != nil {
    // Check specific error types
    if errors.Is(err, ftpclient.ErrorFTPConnection) {
        log.Fatal("Cannot connect to FTP server")
    }
    if errors.Is(err, ftpclient.ErrorFTPLogin) {
        log.Fatal("Invalid credentials")
    }
    log.Fatal("Unexpected error:", err)
}
Automatic Retry Pattern
func uploadWithRetry(client ftpclient.FTPClient, path string, data io.Reader) error {
    maxRetries := 3
    for i := 0; i < maxRetries; i++ {
        err := client.Stor(path, data)
        if err == nil {
            return nil
        }
        
        // Check connection and retry
        if err := client.Check(); err != nil {
            client.Connect() // Try reconnection
        }
        
        time.Sleep(time.Second * time.Duration(i+1))
    }
    return fmt.Errorf("upload failed after %d retries", maxRetries)
}

Use Cases

1. Automated Backup System
// Upload daily backups to FTP server
func backupDatabase(client ftpclient.FTPClient, dbFile string) error {
    // Create backup directory
    date := time.Now().Format("2006-01-02")
    backupDir := fmt.Sprintf("/backups/%s", date)
    client.MakeDir(backupDir)
    
    // Upload compressed backup
    file, err := os.Open(dbFile)
    if err != nil {
        return err
    }
    defer file.Close()
    
    backupPath := fmt.Sprintf("%s/database.sql.gz", backupDir)
    return client.Stor(backupPath, file)
}
2. Log File Collection
// Collect logs from remote servers
func collectLogs(client ftpclient.FTPClient, outputDir string) error {
    entries, err := client.List("/logs")
    if err != nil {
        return err
    }
    
    for _, entry := range entries {
        if entry.Type != 0 { // Skip directories
            continue
        }
        
        // Download log file
        resp, err := client.Retr(fmt.Sprintf("/logs/%s", entry.Name))
        if err != nil {
            continue
        }
        
        // Save locally
        out, _ := os.Create(filepath.Join(outputDir, entry.Name))
        io.Copy(out, resp)
        out.Close()
        resp.Close()
    }
    
    return nil
}
3. File Distribution System
// Distribute files to multiple FTP servers
func distributeFile(servers []string, localPath, remotePath string) error {
    file, err := os.Open(localPath)
    if err != nil {
        return err
    }
    defer file.Close()
    
    var wg sync.WaitGroup
    errors := make(chan error, len(servers))
    
    for _, server := range servers {
        wg.Add(1)
        go func(addr string) {
            defer wg.Done()
            
            cfg := &ftpclient.Config{
                Hostname: addr,
                // ... credentials
            }
            cfg.RegisterContext(func() context.Context {
                return context.Background()
            })
            
            client, err := ftpclient.New(cfg)
            if err != nil {
                errors <- err
                return
            }
            defer client.Close()
            
            // Upload
            file.Seek(0, 0) // Reset file pointer
            if err := client.Stor(remotePath, file); err != nil {
                errors <- err
            }
        }(server)
    }
    
    wg.Wait()
    close(errors)
    
    // Check for errors
    for err := range errors {
        if err != nil {
            return err
        }
    }
    
    return nil
}
4. Data Synchronization
// Sync local directory with FTP server
func syncDirectory(client ftpclient.FTPClient, localDir, remoteDir string) error {
    // Read local files
    localFiles, _ := os.ReadDir(localDir)
    
    // Get remote files
    remoteEntries, err := client.List(remoteDir)
    if err != nil {
        return err
    }
    
    remoteMap := make(map[string]*ftp.Entry)
    for _, entry := range remoteEntries {
        remoteMap[entry.Name] = entry
    }
    
    // Upload new or modified files
    for _, local := range localFiles {
        info, _ := local.Info()
        remotePath := fmt.Sprintf("%s/%s", remoteDir, local.Name())
        
        remote, exists := remoteMap[local.Name()]
        
        // Upload if not exists or size differs
        if !exists || remote.Size != info.Size() {
            file, _ := os.Open(filepath.Join(localDir, local.Name()))
            client.Stor(remotePath, file)
            file.Close()
        }
    }
    
    return nil
}

Performance

Benchmarks

Tested on: Go 1.21, Linux AMD64, localhost FTP server

Operation Time/op Throughput Allocations
Connect ~10ms - 15 allocs
List (100 files) ~50ms 2000 files/s 200 allocs
Upload (1MB) ~100ms 10 MB/s 50 allocs
Download (1MB) ~95ms 10.5 MB/s 45 allocs
NOOP Check ~2ms - 2 allocs

Note: Performance depends on network latency, server capabilities, and file sizes.

Memory Efficiency
  • Streaming I/O: Files are streamed, not loaded entirely in memory
  • Connection Reuse: Single connection per client instance
  • Atomic Operations: Minimal lock contention
Optimization Tips
  1. Reuse Clients: Create one client per server and reuse it
// Good: Reuse client
client, _ := ftpclient.New(cfg)
defer client.Close()
for _, file := range files {
    client.Stor(file, ...)
}

// Bad: Creating new client for each file
for _, file := range files {
    client, _ := ftpclient.New(cfg)
    client.Stor(file, ...)
    client.Close()
}
  1. Appropriate Timeouts: Set reasonable timeouts based on file sizes
cfg.ConnTimeout = 30 * time.Second // For small files
cfg.ConnTimeout = 5 * time.Minute  // For large files
  1. Parallel Uploads: Use goroutines for concurrent operations
var wg sync.WaitGroup
for _, file := range files {
    wg.Add(1)
    go func(f string) {
        defer wg.Done()
        client.Stor(f, ...)
    }(file)
}
wg.Wait()

Best Practices

Security
  1. Never hardcode credentials
// Use environment variables or config files
cfg := &ftpclient.Config{
    Hostname: os.Getenv("FTP_HOST"),
    Login:    os.Getenv("FTP_USER"),
    Password: os.Getenv("FTP_PASS"),
}
  1. Always use TLS for sensitive data
cfg.ForceTLS = true
  1. Validate inputs
if err := cfg.Validate(); err != nil {
    return err
}
Error Handling
  1. Always check errors
if err := client.Stor(path, data); err != nil {
    log.Printf("Upload failed: %v", err)
    return err
}
  1. Use context for timeouts
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

cfg.RegisterContext(func() context.Context {
    return ctx
})
  1. Implement retry logic for transient failures
Resource Management
  1. Always close resources
resp, err := client.Retr("file.txt")
if err != nil {
    return err
}
defer resp.Close() // Important!

client.Close() // When done
  1. Handle large files with streaming
// Stream file instead of loading in memory
resp, _ := client.Retr("large.bin")
defer resp.Close()

out, _ := os.Create("local.bin")
defer out.Close()

io.Copy(out, resp) // Streams data
Concurrency
  1. One client per server, multiple operations
// Thread-safe operations
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(id int) {
        defer wg.Done()
        client.Stor(fmt.Sprintf("file%d.txt", id), ...)
    }(i)
}
wg.Wait()
  1. Connection health checks before critical operations
if err := client.Check(); err != nil {
    client.Connect()
}

Testing

See TESTING.md for comprehensive testing guide.

Quick Test
# Run all tests
go test -v

# Run with race detector
CGO_ENABLED=1 go test -race -v

# Run with coverage
go test -cover -v
Test Coverage

Current coverage: 6.2% (22 tests passing)

Coverage breakdown:

  • Configuration: ~70%
  • Connection management: ~40%
  • File operations: ~5%
  • Directory operations: ~5%

Contributing

Contributions are welcome! Please read our Contributing Guidelines.

Guidelines
  1. No AI for implementation: AI tools should only assist with tests, documentation, and bug fixes
  2. Maintain test coverage: Add tests for new features
  3. Follow Go conventions: Use gofmt, golint, and go vet
  4. Document public APIs: All public types and functions must have GoDoc comments
  5. Update documentation: Keep README.md and TESTING.md in sync with code changes
Development Setup
# Clone repository
git clone https://github.com/nabbar/golib.git
cd golib/ftpclient

# Run tests
go test -v

# Run with race detector
CGO_ENABLED=1 go test -race -v

# Check coverage
go test -cover

License

MIT License - see LICENSE for details

Resources

External Resources

AI Transparency Notice

This documentation was developed with AI assistance for structure, examples, and formatting, under human oversight and validation in compliance with EU AI Act Article 50.4.


Version: 1.0
Last Updated: November 2024
Maintained By: golib Contributors

Documentation

Overview

Package ftpclient provides a thread-safe FTP client with automatic reconnection and connection pooling capabilities.

This package wraps the github.com/jlaffaye/ftp library with additional features:

  • Thread-safe operations using atomic values and mutexes
  • Automatic connection management with health checks (NOOP)
  • Flexible configuration with TLS/SSL support
  • Context-aware operations for timeout control
  • Comprehensive error handling with custom error types

Architecture:

┌──────────────┐
│   FTPClient  │ ← Public Interface
└──────────────┘
       ↓
┌──────────────┐
│  ftpClient   │ ← Thread-safe implementation
│  (atomic)    │
└──────────────┘
       ↓
┌──────────────┐
│ ServerConn   │ ← Underlying FTP connection
└──────────────┘

Basic usage:

import (
    "context"
    "github.com/nabbar/golib/ftpclient"
)

cfg := &ftpclient.Config{
    Hostname:    "ftp.example.com:21",
    Login:       "user",
    Password:    "pass",
    ConnTimeout: 30 * time.Second,
}
cfg.RegisterContext(func() context.Context {
    return context.Background()
})

client, err := ftpclient.New(cfg)
if err != nil {
    panic(err)
}
defer client.Close()

// Upload a file
file, _ := os.Open("local.txt")
defer file.Close()
err = client.Stor("remote.txt", file)

See the Config struct for all available configuration options.

Index

Constants

View Source
const (
	ErrorParamsEmpty        liberr.CodeError = iota + liberr.MinPkgFTPClient // Given parameters are empty
	ErrorValidatorError                                                      // Configuration validation failed
	ErrorEndpointParser                                                      // Cannot parse the given endpoint
	ErrorNotInitialized                                                      // Client instance is not initialized
	ErrorFTPConnection                                                       // Failed to establish FTP connection
	ErrorFTPConnectionCheck                                                  // Connection check (NOOP) failed
	ErrorFTPLogin                                                            // FTP login authentication failed
	ErrorFTPCommand                                                          // FTP command execution failed
)

Error codes for FTP client operations. These errors are registered with the golib/errors package for consistent error handling.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// Hostname define the host/port to connect to server.
	Hostname string `mapstructure:"hostname" json:"hostname" yaml:"hostname" toml:"hostname" validate:"required,hostname_rfc1123"`

	// Login define the login to use in login command.
	Login string `mapstructure:"login" json:"login" yaml:"login" toml:"login"`

	// Password defined the password to use in the login command.
	Password string `mapstructure:"password" json:"password" yaml:"password" toml:"password"`

	// ConnTimeout define a timeout duration for each connection (this is a global connection : connection, store contents, read content, ...).
	ConnTimeout time.Duration `mapstructure:"conn_timeout" json:"conn_timeout" yaml:"conn_timeout" toml:"conn_timeout"`

	// TimeZone force the time zone to use for the connection to the server.
	TimeZone ConfigTimeZone `mapstructure:"timezone" json:"timezone" yaml:"timezone" toml:"timezone"`

	// DisableUTF8 disable the UTF8 translation into the connection to the server.
	DisableUTF8 bool `mapstructure:"disable_utf8" json:"disable_utf8" yaml:"disable_utf8" toml:"disable_utf8"`

	// DisableEPSV disable the EPSV command into the connection to the server. (cf RFC 2428).
	DisableEPSV bool `mapstructure:"disable_epsv" json:"disable_epsv" yaml:"disable_epsv" toml:"disable_epsv"`

	// DisableMLSD disable the MLSD command into the connection to the server. (cf RFC 3659)
	DisableMLSD bool `mapstructure:"disable_mlsd" json:"disable_mlsd" yaml:"disable_mlsd" toml:"disable_mlsd"`

	// EnableMDTM enable the MDTM command into the connection to the server. (cf RFC 3659)
	EnableMDTM bool `mapstructure:"enable_mdtm" json:"enable_mdtm" yaml:"enable_mdtm" toml:"enable_mdtm"`

	// ForceTLS defined if the TLS connection must be forced or not.
	ForceTLS bool `mapstructure:"force_tls" json:"force_tls" yaml:"force_tls" toml:"force_tls"`

	// TLS define the client TLS config used if needed or forced
	TLS libtls.Config `mapstructure:"tls" json:"tls" yaml:"tls" toml:"tls"`
	// contains filtered or unexported fields
}

Config holds the FTP client configuration. It supports various FTP features including TLS, extended passive mode (EPSV), machine-readable listings (MLSD), and file modification time commands (MDTM/MFMT).

The configuration is validated using struct tags and the validator package. All public fields can be marshalled to/from JSON, YAML, TOML, and Viper config formats.

func (*Config) New

func (c *Config) New() (*libftp.ServerConn, error)

New creates a new FTP server connection based on the current configuration. It applies all configured options including TLS, timeouts, timezone, and protocol features. The connection is established and authenticated (if credentials are provided) before returning.

Returns the established connection or an error if connection/authentication fails.

func (*Config) RegisterContext

func (c *Config) RegisterContext(fct func() context.Context)

RegisterContext registers a function that provides context for FTP operations. This allows for timeout and cancellation support in long-running operations. If not registered, operations will not have context support.

func (*Config) RegisterDefaultTLS

func (c *Config) RegisterDefaultTLS(fct func() libtls.TLSConfig)

RegisterDefaultTLS registers a function that provides default TLS configuration. This is used as a fallback when the Config.TLS field is not set. The function should return a TLSConfig that will be used for secure connections.

func (*Config) Validate

func (c *Config) Validate() error

Validate allow checking if the config' struct is valid with the awaiting model

type ConfigTimeZone

type ConfigTimeZone struct {
	Name   string `mapstructure:"name" json:"name" yaml:"name" toml:"name"`         // Timezone name (e.g., "America/New_York", "UTC")
	Offset int    `mapstructure:"offset" json:"offset" yaml:"offset" toml:"offset"` // Offset in seconds from UTC
}

ConfigTimeZone defines the timezone configuration for FTP file timestamps. The offset is specified in seconds from UTC.

type FTPClient

type FTPClient interface {
	// Connect establish the connection to server with the given configuration registered.
	Connect() error

	// Check try to retrieve a valid connection to the server and send an NOOP command to check the connection.
	Check() error

	// Close send the QUID command to the server if the connection is valid (cf Check).
	Close()

	// NameList issues an NLST FTP command.
	NameList(path string) ([]string, error)

	// List issues a LIST FTP command.
	List(path string) ([]*libftp.Entry, error)

	// ChangeDir issues a CWD FTP command, which changes the current directory to the specified path.
	ChangeDir(path string) error

	// CurrentDir issues a PWD FTP command, which Returns the path of the current directory.
	CurrentDir() (string, error)

	// FileSize issues a SIZE FTP command, which Returns the size of the file.
	FileSize(path string) (int64, error)

	// GetTime issues the MDTM FTP command to obtain the file modification time.
	// It returns a UTC time.
	GetTime(path string) (time.Time, error)

	// SetTime issues the MFMT FTP command to set the file modification time.
	// Also it can use a non-standard form of the MDTM command supported by the VsFtpd server instead of MFMT for the same purpose.
	// See "mdtm_write" in https://security.appspot.com/vsftpd/vsftpd_conf.html
	SetTime(path string, t time.Time) error

	// Retr issues a RETR FTP command to fetch the specified file from the remote FTP server.
	// The returned ReadCloser must be closed to cleanup the FTP data connection.
	Retr(path string) (*libftp.Response, error)

	// RetrFrom issues a RETR FTP command to fetch the specified file from the remote FTP server,
	// the server will not send the offset first bytes of the file.
	// The returned ReadCloser must be closed to cleanup the FTP data connection.
	RetrFrom(path string, offset uint64) (*libftp.Response, error)

	// Stor issues a STOR FTP command to store a file to the remote FTP server.
	// Stor creates the specified file with the content of the io.Reader.
	// Hint: io.Pipe() can be used if an io.Writer is required.
	Stor(path string, r io.Reader) error

	// StorFrom issues a STOR FTP command to store a file to the remote FTP server.
	// Stor creates the specified file with the content of the io.Reader, writing on the server will start at the given file offset.
	// Hint: io.Pipe() can be used if an io.Writer is required.
	StorFrom(path string, r io.Reader, offset uint64) error

	// Append issues a APPE FTP command to store a file to the remote FTP server.
	// If a file already exists with the given path, then the content of the io.Reader is appended.
	// Otherwise, a new file is created with that content. Hint: io.Pipe() can be used if an io.Writer is required.
	Append(path string, r io.Reader) error

	// Rename renames a file on the remote FTP server.
	Rename(from, to string) error

	// Delete issues a DELE FTP command to delete the specified file from the remote FTP server.
	Delete(path string) error

	// RemoveDirRecur deletes a non-empty folder recursively using RemoveDir and Delete.
	RemoveDirRecur(path string) error

	// MakeDir issues a MKD FTP command to create the specified directory on the remote FTP server.
	MakeDir(path string) error

	// RemoveDir issues a RMD FTP command to remove the specified directory from the remote FTP server.
	RemoveDir(path string) error

	//Walk prepares the internal walk function so that the caller can begin traversing the directory.
	Walk(root string) (*libftp.Walker, error)
}

FTPClient defines the interface for FTP operations. All methods are thread-safe and handle connection management automatically. Failed connections will be automatically re-established on the next operation.

func New

func New(cfg *Config) (FTPClient, error)

New creates a new FTP client instance with the given configuration. It immediately attempts to connect and validate the connection using a NOOP command.

The client uses atomic operations for thread-safe configuration and connection management. If the initial connection fails, an error is returned and the client is nil.

Example:

cfg := &Config{
    Hostname: "ftp.example.com:21",
    Login:    "user",
    Password: "pass",
}
client, err := New(cfg)
if err != nil {
    log.Fatal(err)
}
defer client.Close()

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL