【问题标题】:Copy file from remote to byte[]将文件从远程复制到字节[]
【发布时间】:2022-01-07 07:16:27
【问题描述】:

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

我已通过参考本指南成功实现上传:https://chuacw.ath.cx/development/b/chuacw/archive/2019/02/04/how-the-scp-protocol-works.aspx

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

有什么建议吗?

func download(con *ssh.Client, buf bytes.Buffer, path string,) ([]byte,error) {
    //https://chuacw.ath.cx/development/b/chuacw/archive/2019/02/04/how-the-scp-protocol-works.aspx
    session, err := con.NewSession()
    if err != nil {
        return nil,err
    }

    buf.WriteString("sudo scp -f " + path + "\n")

    stdin, err := session.StdinPipe()
    if err != nil {
        return nil,err
    }
    go func() {
        defer stdin.Close()
        fmt.Fprint(stdin, "C0660 "+strconv.Itoa(len(content))+" file\n")
        stdin.Write(content)
        fmt.Fprint(stdin, "\x00")
    }()

    output, err := session.CombinedOutput("sudo scp -f " + path)
    buf.Write(output)
    if err != nil {
        return nil,&DeployError{
            Err:    err,
            Output: buf.String(),
        }
    }

    session.Close()

    session, err = con.NewSession()
    if err != nil {
        return nil,err
    }
    defer session.Close()

    return output,nil
}

【问题讨论】:

  • 您是否有理由尝试手动实现 scp 协议,而不是使用现有库或更标准的协议?
  • 很高兴知道如何“实施”它
  • 您是否实现了 HTTP 而不是使用 http 库?如果您要求学习如何实现它,那很好 - 但您至少需要尝试实现它,然后我们可以帮助您解决可能遇到的问题。
  • 如您所见,我已经为uploading 实现了它。我不明白为了我所问的有什么关系。它应该可以工作,但我在此过程中遗漏了一些东西。我不想使用任何包wahtsoever。

标签: go scp


【解决方案1】:

接收端比源端困难得多。做了一个例子,应该让你接近你想要的。请注意,我没有测试过这段代码,错误处理是次优的,它只支持 SCP 可能使用的协议消息的 1/4。所以你仍然需要做一些工作才能让它变得完美。

说了这么多,这就是我想出的:

func download(con *ssh.Client, path string) ([]byte, error) {
    //https://chuacw.ath.cx/development/b/chuacw/archive/2019/02/04/how-the-scp-protocol-works.aspx
    session, err := con.NewSession()
    if err != nil {
        return nil, err
    }
    defer session.Close()

    // Local -> remote
    stdin, err := session.StdinPipe()
    if err != nil {
        return nil, err
    }
    defer stdin.Close()

    // Request a file, note that directories will require different handling
    _, err = stdin.Write([]byte("sudo scp -f " + path + "\n"))
    if err != nil {
        return nil, err
    }

    // Remote -> local
    stdout, err := session.StdoutPipe()
    if err != nil {
        return nil, err
    }

    // Make a buffer for the protocol messages
    const megabyte = 1 << 20
    b := make([]byte, megabyte)

    // Offset into the buffer
    off := 0

    var filesize int64

    // SCP may send multiple protocol messages, so keep reading
    for {
        n, err := stdout.Read(b[off:])
        if err != nil {
            return nil, err
        }

        nl := bytes.Index(b[:off+n], []byte("\n"))
        // If there is no newline in the buffer, we need to read more
        if nl == -1 {
            off = off + n
            continue
        }

        // We read a full message, reset the offset
        off = 0

        // if we did get a new line. We have the full protocol message
        msg := string(b[:nl])

        // Send back 0, which means OK, the SCP source will not send the next message otherwise
        _, err = stdin.Write([]byte("0\n"))
        if err != nil {
            return nil, err
        }

        // First char is the mode (C=file, D=dir, E=End of dir, T=Time metadata)
        mode := msg[0]
        if mode != 'C' {
            // Ignore other messags for now.
            continue
        }

        // File message = Cmmmm <length> <filename>
        msgParts := strings.Split(msg, " ")
        if len(msgParts) > 1 {
            // Parse the second part <length> as an base 10 integer
            filesize, err = strconv.ParseInt(msgParts[1], 10, 64)
            if err != nil {
                return nil, err
            }
        }

        // The file message will be followed with binary data containing the file
        break
    }

    // Wrap the stdout reader in a limit reader so we will not read more than the filesize
    fileReader := io.LimitReader(stdout, filesize)
    // Seed the bytes buffer with the existing byte slice, saves additional allocation if file <= 1mb
    buf := bytes.NewBuffer(b)

    // Copy the file into the bytes buffer
    _, err = io.Copy(buf, fileReader)
    return buf.Bytes(), err
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-01
    • 2018-10-04
    • 2020-06-05
    • 2016-05-23
    • 2017-05-02
    • 1970-01-01
    • 2012-07-09
    • 2020-02-25
    相关资源
    最近更新 更多