从远程复制文件到字节数组。

huangapple go评论119阅读模式
英文:

Copy file from remote to byte[]

问题

我正在尝试弄清楚如何实现从远程复制文件并从缓冲区获取数据[]byte

我已经成功地通过参考这个指南实现了上传的部分:https://chuacw.ath.cx/development/b/chuacw/archive/2019/02/04/how-the-scp-protocol-works.aspx

go func内部,有SCPupload过程的实现,但我不知道如何修改它。

有什么建议吗?

  1. func download(con *ssh.Client, buf bytes.Buffer, path string,) ([]byte,error) {
  2. //https://chuacw.ath.cx/development/b/chuacw/archive/2019/02/04/how-the-scp-protocol-works.aspx
  3. session, err := con.NewSession()
  4. if err != nil {
  5. return nil,err
  6. }
  7. buf.WriteString("sudo scp -f " + path + "\n")
  8. stdin, err := session.StdinPipe()
  9. if err != nil {
  10. return nil,err
  11. }
  12. go func() {
  13. defer stdin.Close()
  14. fmt.Fprint(stdin, "C0660 "+strconv.Itoa(len(content))+" file\n")
  15. stdin.Write(content)
  16. fmt.Fprint(stdin, "\x00")
  17. }()
  18. output, err := session.CombinedOutput("sudo scp -f " + path)
  19. buf.Write(output)
  20. if err != nil {
  21. return nil,&DeployError{
  22. Err: err,
  23. Output: buf.String(),
  24. }
  25. }
  26. session.Close()
  27. session, err = con.NewSession()
  28. if err != nil {
  29. return nil,err
  30. }
  31. defer session.Close()
  32. return output,nil
  33. }
英文:

I'm trying to figure out how to implement copying files from remote and get the data []byte from the buffer.

I have succeeded in doing the implementation with the upload by referring to this guide: https://chuacw.ath.cx/development/b/chuacw/archive/2019/02/04/how-the-scp-protocol-works.aspx

Inside the go func there's the implementation of the upload process of the SCP but I have no idea how to change it.

Any advice ?

  1. func download(con *ssh.Client, buf bytes.Buffer, path string,) ([]byte,error) {
  2. //https://chuacw.ath.cx/development/b/chuacw/archive/2019/02/04/how-the-scp-protocol-works.aspx
  3. session, err := con.NewSession()
  4. if err != nil {
  5. return nil,err
  6. }
  7. buf.WriteString("sudo scp -f " + path + "\n")
  8. stdin, err := session.StdinPipe()
  9. if err != nil {
  10. return nil,err
  11. }
  12. go func() {
  13. defer stdin.Close()
  14. fmt.Fprint(stdin, "C0660 "+strconv.Itoa(len(content))+" file\n")
  15. stdin.Write(content)
  16. fmt.Fprint(stdin, "\x00")
  17. }()
  18. output, err := session.CombinedOutput("sudo scp -f " + path)
  19. buf.Write(output)
  20. if err != nil {
  21. return nil,&DeployError{
  22. Err: err,
  23. Output: buf.String(),
  24. }
  25. }
  26. session.Close()
  27. session, err = con.NewSession()
  28. if err != nil {
  29. return nil,err
  30. }
  31. defer session.Close()
  32. return output,nil
  33. }

答案1

得分: 1

下面是翻译好的内容:

  1. func download(con *ssh.Client, path string) ([]byte, error) {
  2. //https://chuacw.ath.cx/development/b/chuacw/archive/2019/02/04/how-the-scp-protocol-works.aspx
  3. session, err := con.NewSession()
  4. if err != nil {
  5. return nil, err
  6. }
  7. defer session.Close()
  8. // 本地 -> 远程
  9. stdin, err := session.StdinPipe()
  10. if err != nil {
  11. return nil, err
  12. }
  13. defer stdin.Close()
  14. // 请求一个文件,注意目录需要不同的处理方式
  15. _, err = stdin.Write([]byte("sudo scp -f " + path + "\n"))
  16. if err != nil {
  17. return nil, err
  18. }
  19. // 远程 -> 本地
  20. stdout, err := session.StdoutPipe()
  21. if err != nil {
  22. return nil, err
  23. }
  24. // 创建一个用于协议消息的缓冲区
  25. const megabyte = 1 << 20
  26. b := make([]byte, megabyte)
  27. // 缓冲区偏移量
  28. off := 0
  29. var filesize int64
  30. // SCP 可能会发送多个协议消息,因此需要持续读取
  31. for {
  32. n, err := stdout.Read(b[off:])
  33. if err != nil {
  34. return nil, err
  35. }
  36. nl := bytes.Index(b[:off+n], []byte("\n"))
  37. // 如果缓冲区中没有换行符,需要继续读取
  38. if nl == -1 {
  39. off = off + n
  40. continue
  41. }
  42. // 读取完整的消息后,重置偏移量
  43. off = 0
  44. // 如果有换行符,表示已经获取到完整的协议消息
  45. msg := string(b[:nl])
  46. // 发送 0,表示 OK,否则 SCP 源不会发送下一条消息
  47. _, err = stdin.Write([]byte("0\n"))
  48. if err != nil {
  49. return nil, err
  50. }
  51. // 第一个字符是模式(C=文件,D=目录,E=目录结束,T=时间元数据)
  52. mode := msg[0]
  53. if mode != 'C' {
  54. // 目前忽略其他消息
  55. continue
  56. }
  57. // 文件消息 = Cmmmm <length> <filename>
  58. msgParts := strings.Split(msg, " ")
  59. if len(msgParts) > 1 {
  60. // 将第二部分 <length> 解析为十进制整数
  61. filesize, err = strconv.ParseInt(msgParts[1], 10, 64)
  62. if err != nil {
  63. return nil, err
  64. }
  65. }
  66. // 文件消息后面是包含文件内容的二进制数据
  67. break
  68. }
  69. // 使用限制读取器将 stdout 读取器包装起来,以确保不会读取超过文件大小的内容
  70. fileReader := io.LimitReader(stdout, filesize)
  71. // 使用现有的字节切片初始化字节缓冲区,如果文件大小 <= 1MB,则节省额外的分配
  72. buf := bytes.NewBuffer(b)
  73. // 将文件复制到字节缓冲区中
  74. _, err = io.Copy(buf, fileReader)
  75. return buf.Bytes(), err
  76. }
英文:

The sink side is significantly more difficult than the source side. Made an example which should get you close to what you want. Note that I have not tested this code, that the error handling is sub optimal and it only supports 1/4th the protocol messages SCP may use. So you will still need to do some work to get it perfect.

With all that said, this is what I came up with:

  1. func download(con *ssh.Client, path string) ([]byte, error) {
  2. //https://chuacw.ath.cx/development/b/chuacw/archive/2019/02/04/how-the-scp-protocol-works.aspx
  3. session, err := con.NewSession()
  4. if err != nil {
  5. return nil, err
  6. }
  7. defer session.Close()
  8. // Local -&gt; remote
  9. stdin, err := session.StdinPipe()
  10. if err != nil {
  11. return nil, err
  12. }
  13. defer stdin.Close()
  14. // Request a file, note that directories will require different handling
  15. _, err = stdin.Write([]byte(&quot;sudo scp -f &quot; + path + &quot;\n&quot;))
  16. if err != nil {
  17. return nil, err
  18. }
  19. // Remote -&gt; local
  20. stdout, err := session.StdoutPipe()
  21. if err != nil {
  22. return nil, err
  23. }
  24. // Make a buffer for the protocol messages
  25. const megabyte = 1 &lt;&lt; 20
  26. b := make([]byte, megabyte)
  27. // Offset into the buffer
  28. off := 0
  29. var filesize int64
  30. // SCP may send multiple protocol messages, so keep reading
  31. for {
  32. n, err := stdout.Read(b[off:])
  33. if err != nil {
  34. return nil, err
  35. }
  36. nl := bytes.Index(b[:off+n], []byte(&quot;\n&quot;))
  37. // If there is no newline in the buffer, we need to read more
  38. if nl == -1 {
  39. off = off + n
  40. continue
  41. }
  42. // We read a full message, reset the offset
  43. off = 0
  44. // if we did get a new line. We have the full protocol message
  45. msg := string(b[:nl])
  46. // Send back 0, which means OK, the SCP source will not send the next message otherwise
  47. _, err = stdin.Write([]byte(&quot;0\n&quot;))
  48. if err != nil {
  49. return nil, err
  50. }
  51. // First char is the mode (C=file, D=dir, E=End of dir, T=Time metadata)
  52. mode := msg[0]
  53. if mode != &#39;C&#39; {
  54. // Ignore other messags for now.
  55. continue
  56. }
  57. // File message = Cmmmm &lt;length&gt; &lt;filename&gt;
  58. msgParts := strings.Split(msg, &quot; &quot;)
  59. if len(msgParts) &gt; 1 {
  60. // Parse the second part &lt;length&gt; as an base 10 integer
  61. filesize, err = strconv.ParseInt(msgParts[1], 10, 64)
  62. if err != nil {
  63. return nil, err
  64. }
  65. }
  66. // The file message will be followed with binary data containing the file
  67. break
  68. }
  69. // Wrap the stdout reader in a limit reader so we will not read more than the filesize
  70. fileReader := io.LimitReader(stdout, filesize)
  71. // Seed the bytes buffer with the existing byte slice, saves additional allocation if file &lt;= 1mb
  72. buf := bytes.NewBuffer(b)
  73. // Copy the file into the bytes buffer
  74. _, err = io.Copy(buf, fileReader)
  75. return buf.Bytes(), err
  76. }

huangapple
  • 本文由 发表于 2021年11月30日 23:11:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/70171643.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定