func (client *Client) PosixRename(oldname, newname string) error {
	return client.SFTPClient.PosixRename(oldname, newname)
}
PosixRename renames a file using the posix-rename@openssh.com extension
which will replace newname if it already exists.
package sftp
import (
	"io"
	"os"
	"path/filepath"
	"time"
	"github.com/abulo/ratel/v2/logger"
	"github.com/pkg/errors"
	"github.com/pkg/sftp"
	"golang.org/x/crypto/ssh"
)
type Config struct {
	Host     string
	Port     string
	User     string
	Password string
	Timeout  time.Duration
}
type Client struct {
	SSHClient  *ssh.Client
	SFTPClient *sftp.Client
}
type IOReaderProgress struct {
	io.Reader
	TransferredBytes *int64  
}
func (iorp *IOReaderProgress) Read(p []byte) (int, error) {
	n, err := iorp.Reader.Read(p)
	*iorp.TransferredBytes += int64(n)
	return n, err
}
func (config *Config) New() (*Client, error) {
	clientConfig := &ssh.ClientConfig{
		User: config.User,
		Auth: []ssh.AuthMethod{
			ssh.Password(config.Password),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
		Timeout:         config.Timeout,
	}
	conn, err := ssh.Dial("tcp", config.Host+":"+config.Port, clientConfig)
	if err != nil {
		logger.Logger.Error(err)
		return nil, err
	}
	client, err := sftp.NewClient(conn)
	if err != nil {
		logger.Logger.Error(err)
		return nil, err
	}
	return &Client{
		SSHClient:  conn,
		SFTPClient: client,
	}, nil
}
func (client *Client) DownFile(serverPath, destPath string) (int64, error) {
	return getTransfer(client.SFTPClient, serverPath, destPath, nil, nil)
}
func (client *Client) DownFileWithProgress(serverPath, destPath string, transferred *int64, total *int64) (int64, error) {
	return getTransfer(client.SFTPClient, serverPath, destPath, transferred, total)
}
func (client *Client) GetRecursively(remotePath, localPath string) error {
	remoteWalker := client.SFTPClient.Walk(remotePath)
	if remoteWalker == nil {
		return errors.New("SFTP Walker Error")
	}
	for remoteWalker.Step() {
		err := remoteWalker.Err()
		if err != nil {
			return err
		}
		remoteFullFilepath := remoteWalker.Path()
		localFilepath, _ := getRecursivelyPath(localPath, remotePath, remoteFullFilepath)
		if remoteWalker.Stat().IsDir() {
			localStat, localStatErr := os.Stat(localFilepath)
			if !os.IsNotExist(localStatErr) && !localStat.IsDir() {
				return errors.New("Cannot create a directry when that file already exists")
			}
			mode := remoteWalker.Stat().Mode()
			if os.IsNotExist(localStatErr) {
				mkErr := os.Mkdir(localFilepath, mode)
				if mkErr != nil {
					return mkErr
				}
			}
			continue
		}
		_, getErr := getTransfer(client.SFTPClient, localFilepath, remoteFullFilepath, nil, nil)
		if getErr != nil {
			return getErr
		}
	}
	return nil
}
func getTransfer(client *sftp.Client, remoteFilepath, localFilepath string, tfBytes *int64, totalBytes *int64) (int64, error) {
	localFile, localFileErr := os.Create(localFilepath)
	if localFileErr != nil {
		return 0, errors.New("localFileErr: " + localFileErr.Error())
	}
	defer localFile.Close()
	remoteFile, remoteFileErr := client.Open(remoteFilepath)
	if remoteFileErr != nil {
		return 0, errors.New("remoteFileErr: " + remoteFileErr.Error())
	}
	defer remoteFile.Close()
	var bytes int64
	var copyErr error
	if totalBytes != nil {
		f, _ := remoteFile.Stat()
		*totalBytes = f.Size()
	}
	if tfBytes != nil {
		remoteFileWithProgress := &IOReaderProgress{Reader: remoteFile, TransferredBytes: tfBytes}
		bytes, copyErr = io.Copy(localFile, remoteFileWithProgress)
	} else {
		bytes, copyErr = io.Copy(localFile, remoteFile)
	}
	if copyErr != nil {
		return 0, errors.New("copyErr: " + copyErr.Error())
	}
	syncErr := localFile.Sync()
	if syncErr != nil {
		return 0, errors.New("syncErr: " + syncErr.Error())
	}
	return bytes, nil
}
func (client *Client) UploadFile(localFilepath string, remoteFilepath string) (int64, error) {
	return putTransfer(client.SFTPClient, localFilepath, remoteFilepath, nil, nil)
}
func (client *Client) UploadFileWithProgress(localFilepath string, remoteFilepath string, transferred *int64, total *int64) (int64, error) {
	return putTransfer(client.SFTPClient, localFilepath, remoteFilepath, transferred, total)
}
func (client *Client) PutRecursively(localPath string, remotePath string) error {
	localWalkerErr := filepath.Walk(localPath, func(localFullFilepath string, info os.FileInfo, fileErr error) error {
		if fileErr != nil {
			return fileErr
		}
		remoteFilepath, _ := getRecursivelyPath(remotePath, localPath, localFullFilepath)
		if info.IsDir() {
			remoteStat, remoteStatErr := client.SFTPClient.Stat(remoteFilepath)
			if !os.IsNotExist(remoteStatErr) && !remoteStat.IsDir() {
				return errors.New("Cannot create a directry when that file already exists")
			}
			mode := info.Mode()
			if os.IsNotExist(remoteStatErr) {
				mkErr := client.SFTPClient.Mkdir(remoteFilepath)
				if mkErr != nil {
					return mkErr
				}
				chErr := client.SFTPClient.Chmod(remoteFilepath, mode)
				if chErr != nil {
					return chErr
				}
			}
			return nil
		}
		_, getErr := putTransfer(client.SFTPClient, localFullFilepath, remoteFilepath, nil, nil)
		if getErr != nil {
			return getErr
		}
		return nil
	})
	return localWalkerErr
}
func putTransfer(client *sftp.Client, localFilepath string, remoteFilepath string, tfBytes *int64, totalBytes *int64) (int64, error) {
	remoteFile, remoteFileErr := client.Create(remoteFilepath)
	if remoteFileErr != nil {
		return 0, errors.New("remoteFileErr: " + remoteFileErr.Error())
	}
	defer remoteFile.Close()
	localFile, localFileErr := os.Open(localFilepath)
	if localFileErr != nil {
		return 0, errors.New("localFileErr: " + localFileErr.Error())
	}
	defer localFile.Close()
	var bytes int64
	var copyErr error
	if totalBytes != nil {
		f, _ := localFile.Stat()
		*totalBytes = f.Size()
	}
	if tfBytes != nil {
		localFileWithProgress := &IOReaderProgress{Reader: localFile, TransferredBytes: tfBytes}
		bytes, copyErr = io.Copy(remoteFile, localFileWithProgress)
	} else {
		bytes, copyErr = io.Copy(remoteFile, localFile)
	}
	if copyErr != nil {
		return 0, errors.New("copyErr: " + copyErr.Error())
	}
	return bytes, nil
}
func (client *Client) Close() (errors []error) {
	if client.SFTPClient != nil {
		sftpErr := client.SFTPClient.Close()
		if sftpErr != nil {
			errors = append(errors, sftpErr)
		}
	}
	if client.SSHClient != nil {
		sshErr := client.SSHClient.Close()
		if sshErr != nil {
			errors = append(errors, sshErr)
		}
	}
	return
}
func getRecursivelyPath(localPath string, remotePath string, remoteFullFilepath string) (string, error) {
	rel, err := filepath.Rel(filepath.Clean(remotePath), remoteFullFilepath)
	if err != nil {
		return "", err
	}
	localFilepath := filepath.Join(localPath, rel)
	return filepath.ToSlash(localFilepath), nil
}
func (client *Client) Quit() (errors []error) {
	return client.Close()
}
func (client *Client) Chmod(path string, mode os.FileMode) error {
	return client.SFTPClient.Chmod(path, mode)
}
func (client *Client) Chown(path string, uid, gid int) error {
	return client.SFTPClient.Chown(path, uid, gid)
}
func (client *Client) Remove(path string) error {
	return client.SFTPClient.Remove(path)
}
func (client *Client) RemoveDirectory(path string) error {
	return client.SFTPClient.RemoveDirectory(path)
}
func (client *Client) Rename(oldname, newname string) error {
	return client.SFTPClient.Rename(oldname, newname)
}