import “golang.org/x/crypto/ssh”
文档 https://godoc.org/github.com/pkg/sftp

type Client

func NewClient(conn ssh.Client, opts …ClientOption) (Client, error)
func (c Client) Chmod(path string, mode os.FileMode) error 更改指定文件的权限
func (c
Client) Chown(path string, uid, gid int) error 更改指定文件的用户和组所有者
func (c Client) Chtimes(path string, atime time.Time, mtime time.Time) error 更改指定文件的访问和修改时间func (c Client) Close() error
func (c Client) Create(path string) (File, error) 以0666权限创建文件并打开,如果该文件存在相当于删除后再打开

func (c Client) Getwd() (string, error) 返回当前工作目录
func (c
Client) Glob(pattern string) (matches []string, err error) 返回所有匹配模式的文件的名称
func (c Client) Link(oldname, newname string) error 创建link
func (c
Client) Mkdir(path string) error 创建文件夹,如果指定路径的文件或目录已经存在,或者该目录的父文件夹不存在,返回错误
func (c Client) MkdirAll(path string) error 创建一个名为path的目录,以及所有必要的父目录,并返回nil
func (c
Client) Open(path string) (File, error) 以只读打开一个文件
func (c
Client) ReadDir(p string) ([]os.FileInfo, error) 打开文件夹
func (c Client) ReadLink(p string) (string, error) 读取符号链接的目标
func (c
Client) Remove(path string) error 删除文件或目录。如果路径不存在,或目录不是空的,则返回错误。
func (c Client) RemoveDirectory(path string) error 递归删除
func (c
Client) Rename(oldname, newname string) error 重命名
func (c *Client) Stat(p string) (os.FileInfo, error) Stat返回一个FileInfo结构,描述路径“p”指定的文件。如果“p”是一个符号链接,则返回的FileInfo结构将描述引用文件。

type ClientOption

  1. type ClientOption func(*Client) error

func MaxConcurrentRequestsPerFile(n int) ClientOption 置单个文件允许的最大并发请求。默认64
func MaxPacket(size int) ClientOption
func MaxPacketChecked(size int) ClientOption
func MaxPacketUnchecked(size int) ClientOption
func UseFstat(value bool) ClientOption

type File

func (f File) Chmod(mode os.FileMode) error
func (f
File) Chown(uid, gid int) error
func (f File) Close() error
func (f
File) Name() string
func (f File) Read(b []byte) (int, error)
func (f
File) ReadFrom(r io.Reader) (int64, error) 读取数据并将其写入文件(这种方法比多次调用Write更可取)
func (f File) Stat() (os.FileInfo, error)
func (f
File) Write(b []byte) (int, error)
func (f *File) WriteTo(w io.Writer) (int64, error) 将文件写入w

例子:

  1. package service
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/pkg/sftp"
  6. "golang.org/x/crypto/ssh"
  7. "io"
  8. "io/ioutil"
  9. "net"
  10. "os"
  11. "path"
  12. "strings"
  13. "time"
  14. )
  15. type SshServer struct {
  16. }
  17. // 连接到ssh的struct
  18. type SshInfoModel struct {
  19. User string
  20. PassWord string
  21. Host string
  22. Port string
  23. KeyPath string //暂时不用到,之后可能用密钥进行连接
  24. }
  25. /**
  26. * SSH连接 超时参数(秒)
  27. */
  28. const SSH_CONNECT_TIMEOUT time.Duration = 30
  29. /**
  30. * SSH连接
  31. */
  32. func SSHConnect(username, password, host string, port int) (*ssh.Client, error) {
  33. var (
  34. sshClient *ssh.Client
  35. clientConfig *ssh.ClientConfig
  36. err error
  37. )
  38. clientConfig = &ssh.ClientConfig{
  39. User: username,
  40. Auth: []ssh.AuthMethod{
  41. ssh.Password(password),
  42. },
  43. //需要验证服务端,不做验证返回nil就可以,点击HostKeyCallback看源码就知道了
  44. HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
  45. // fmt.Println("============>", hostname, remote, key)
  46. return nil
  47. },
  48. Timeout: SSH_CONNECT_TIMEOUT * time.Second,
  49. }
  50. // connet to ssh
  51. if sshClient, err = ssh.Dial("tcp", fmt.Sprintf("%s:%d", host, port), clientConfig); err != nil {
  52. return nil, err
  53. }
  54. return sshClient, nil
  55. }
  56. /**
  57. * SSH客户端获取Session客户端
  58. */
  59. func GetSSHClientSession(sshClient *ssh.Client) (*ssh.Session, error) {
  60. var sessionClient *ssh.Session
  61. var err error
  62. // create session
  63. if sessionClient, err = sshClient.NewSession(); err != nil {
  64. return nil, err
  65. }
  66. return sessionClient, nil
  67. }
  68. /**
  69. * SSH客户端获取Ftp客户端
  70. */
  71. func GetSSHClientFtp(sshClient *ssh.Client) (*sftp.Client, error) {
  72. var sftpClient *sftp.Client
  73. var err error
  74. // create sftp client
  75. if sftpClient, err = sftp.NewClient(sshClient); err != nil {
  76. return nil, err
  77. }
  78. return sftpClient, nil
  79. }
  80. /**
  81. * SSH客户端Ftp上传文件到目标服务端(注意是上传文件)
  82. * localFilePath:客户端文件位置
  83. * remotePath:上传的目标路径
  84. */
  85. func SFTPUploadFile(sftpClient *sftp.Client, localFilePath string, remotePath string) error {
  86. srcFile, err := os.Open(localFilePath)
  87. if err != nil {
  88. return err
  89. }
  90. defer srcFile.Close()
  91. var remoteFileName = path.Base(localFilePath)
  92. dstFile, err := sftpClient.Create(path.Join(remotePath, remoteFileName))
  93. if err != nil {
  94. return err
  95. }
  96. defer dstFile.Close()
  97. buf := make([]byte, 1024)
  98. for {
  99. n, _ := srcFile.Read(buf)
  100. if n == 0 {
  101. break
  102. }
  103. dstFile.Write(buf[0:n])
  104. }
  105. return nil
  106. }
  107. /**
  108. * SSH客户端Ftp上传文件夹到目标服务端(注意是上传文件夹)
  109. * localFile:客户端文件夹位置
  110. * remotePath:上传的目标路径
  111. */
  112. func SFTPUploadDirectory(sftpClient *sftp.Client, localPath string, remotePath string) {
  113. localFiles, err := ioutil.ReadDir(localPath)
  114. if err != nil {
  115. return
  116. }
  117. for _, backupDir := range localFiles {
  118. localFilePath := path.Join(localPath, backupDir.Name())
  119. remoteFilePath := path.Join(remotePath, backupDir.Name())
  120. if backupDir.IsDir() {
  121. err = sftpClient.Mkdir(remoteFilePath)
  122. if err != nil {
  123. return
  124. }
  125. SFTPUploadDirectory(sftpClient, localFilePath, remoteFilePath)
  126. } else {
  127. err = SFTPUploadFile(sftpClient, path.Join(localPath, backupDir.Name()), remotePath)
  128. if err != nil {
  129. return
  130. }
  131. }
  132. }
  133. }
  134. /**
  135. * SSH客户端Ftp上传文件到目标服务端(注意是上传文件)
  136. * io.Reader:文件流
  137. * allPath:上传的目标路径含文件名
  138. */
  139. func SFTPUploadFileReader(sftpClient *sftp.Client, file io.Reader, allPath string) error {
  140. dstFile, err := sftpClient.Create(allPath)
  141. if err != nil {
  142. return err
  143. }
  144. defer dstFile.Close()
  145. buf := make([]byte, 1024)
  146. for {
  147. n, _ := file.Read(buf)
  148. if n == 0 {
  149. break
  150. }
  151. dstFile.Write(buf[0:n])
  152. }
  153. return nil
  154. }

ftp使用记录

ftp传包比scp慢很多,确定不是带宽问题,经过排查是缓冲区给太少的原因,代码如下

  1. /**
  2. * SSH客户端Ftp上传文件到目标服务端(注意是上传文件)
  3. * localFilePath:客户端文件位置
  4. * remotePath:上传的目标路径
  5. */
  6. func SFTPUploadFile(sftpClient *sftp.Client, localFilePath string, remotePath string) error {
  7. srcFile, err := os.Open(localFilePath)
  8. if err != nil {
  9. return err
  10. }
  11. defer srcFile.Close()
  12. var remoteFileName = path.Base(localFilePath)
  13. dstFile, err := sftpClient.Create(path.Join(remotePath, remoteFileName))
  14. if err != nil {
  15. return err
  16. }
  17. defer dstFile.Close()
  18. buf := make([]byte, 1024) // 将缓冲区扩大100倍,及1024*100即可
  19. for {
  20. n, _ := srcFile.Read(buf)
  21. if n == 0 {
  22. break
  23. }
  24. dstFile.Write(buf[0:n])
  25. }
  26. return nil
  27. }