func (cl *Client) Get(remoteFile, localFile string) (err error) {
	var (
		logp   = "Get"
		offset uint64
	)
	fin, err := cl.Open(remoteFile)
	if err != nil {
		return fmt.Errorf("%s: %w", logp, err)
	}
	fout, err := os.Create(localFile)
	if err != nil {
		return fmt.Errorf("%s: %w", logp, err)
	}
	for {
		data, err := cl.Read(fin, offset)
		if len(data) > 0 {
			_, err = fout.Write(data)
			if err != nil {
				break
			}
		}
		if err != nil {
			break
		}
		offset += uint64(len(data))
	}
	if err != nil && !errors.Is(err, io.EOF) {
		return fmt.Errorf("%s: %w", logp, err)
	}
	err = fout.Close()
	if err != nil {
		return fmt.Errorf("%s: %w", logp, err)
	}
	err = cl.Close(fin)
	if err != nil {
		return fmt.Errorf("%s: %w", logp, err)
	}
	return err
}
Get copy remote file to local.
The local file will be created if its not exist; otherwise it will
truncated.
package sftp
import (
	"errors"
	"fmt"
	"io"
	"io/fs"
	"os"
	"sync"
	"time"
	"golang.org/x/crypto/ssh"
)
type Client struct {
	sess    *ssh.Session
	version uint32
	exts    extensions
	pipeIn  io.WriteCloser
	pipeOut io.Reader
	pipeErr io.Reader
	mtxId     sync.Mutex
	requestId uint32
}
func NewClient(sshc *ssh.Client) (cl *Client, err error) {
	logp := "New"
	cl = &Client{}
	cl.sess, err = sshc.NewSession()
	if err != nil {
		return nil, fmt.Errorf("%s: NewSession: %w", logp, err)
	}
	cl.pipeIn, err = cl.sess.StdinPipe()
	if err != nil {
		return nil, fmt.Errorf("%s: StdinPipe: %w", logp, err)
	}
	cl.pipeOut, err = cl.sess.StdoutPipe()
	if err != nil {
		return nil, fmt.Errorf("%s: StdoutPipe: %w", logp, err)
	}
	cl.pipeErr, err = cl.sess.StderrPipe()
	if err != nil {
		return nil, fmt.Errorf("%s: StderrPipe: %w", logp, err)
	}
	err = cl.sess.RequestSubsystem(subsystemNameSftp)
	if err != nil {
		if err.Error() == "ssh: subsystem request failed" {
			return nil, ErrSubsystem
		}
		return nil, fmt.Errorf("%s: RequestSubsystem: %w", logp, err)
	}
	cl.requestId = uint32(time.Now().Unix())
	err = cl.init()
	if err != nil {
		return nil, fmt.Errorf("%s: %w", logp, err)
	}
	return cl, nil
}
func (cl *Client) Close(fh *FileHandle) (err error) {
	if fh == nil {
		return nil
	}
	var (
		logp    = "Close"
		req     = cl.generatePacket()
		payload = req.fxpClose(fh)
	)
	res, err := cl.send(payload)
	if err != nil {
		return fmt.Errorf("%s: %w", logp, err)
	}
	if res.kind != packetKindFxpStatus {
		return errUnexpectedResponse(packetKindFxpStatus, res.kind)
	}
	if res.code != statusCodeOK {
		return handleStatusCode(res.code, res.message)
	}
	return nil
}
func (cl *Client) Create(remoteFile string, fa *FileAttrs) (*FileHandle, error) {
	pflags := OpenFlagWrite | OpenFlagCreate | OpenFlagTruncate
	return cl.OpenFile(remoteFile, pflags, fa)
}
func (cl *Client) Fsetstat(fh *FileHandle, fa *FileAttrs) (err error) {
	var (
		logp = "Fsetstat"
		req  = cl.generatePacket()
	)
	if fh == nil || fa == nil {
		return nil
	}
	payload := req.fxpFsetstat(fh, fa)
	res, err := cl.send(payload)
	if err != nil {
		return fmt.Errorf("%s: %w", logp, err)
	}
	if res.kind != packetKindFxpStatus {
		return errUnexpectedResponse(packetKindFxpStatus, res.kind)
	}
	if res.code != statusCodeOK {
		return handleStatusCode(res.code, res.message)
	}
	return nil
}
func (cl *Client) Fstat(fh *FileHandle) (fa *FileAttrs, err error) {
	var (
		logp    = "Fstat"
		req     = cl.generatePacket()
		payload = req.fxpFstat(fh)
	)
	res, err := cl.send(payload)
	if err != nil {
		return nil, fmt.Errorf("%s: %w", logp, err)
	}
	if res.kind == packetKindFxpStatus {
		return nil, handleStatusCode(res.code, res.message)
	}
	if res.kind != packetKindFxpAttrs {
		return nil, errUnexpectedResponse(packetKindFxpAttrs, res.kind)
	}
	fa = res.fa
	fa.name = fh.remotePath
	res.fa = nil
	return fa, nil
}