如何使用Go语言传输多个文件

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

How to transfer multiple files using go

问题

我正在尝试用Go语言编写一个程序,它分为两部分。其中一部分是客户端,试图将多个图片上传到另一部分的服务器。

服务器端应该执行以下操作:

  1. 获取将要发送的文件数量
  2. 循环处理每个文件
  3. 获取文件名
  4. 获取文件并保存
  5. 转到步骤3

到目前为止,服务器端正在执行以下操作:

func getFileFromClient(connection net.Conn) {
    var numberOfPics int
    var err error
    var receivedBytes int64
    var fileName string
    r := bufio.NewReader(connection)
    strNumberOfPics, err := r.ReadString('\n')
    if err != nil {
        fmt.Printf("读取错误:%s\n", err)
        return
    }
    fmt.Printf("读取:%s\n", strNumberOfPics)
    strNumberOfPics = strings.Trim(strNumberOfPics, "\n")
    numberOfPics, err = strconv.Atoi(strNumberOfPics)
    if err != nil {
        fmt.Printf("Atoi错误:%s\n", err)
        panic("Atoi")
    }
    fmt.Printf("接收 %d 张图片:\n", numberOfPics)
    for i := 0; i < numberOfPics; i++ {
        // 获取文件名:
        fileName, err = r.ReadString('\n')
        if err != nil {
            fmt.Printf("接收错误:%s\n", err)
        }
        fmt.Printf("文件名:%s\n", fileName)
        fileName = strings.Trim(fileName, "\n")
        f, err := os.Create(fileName)
        defer f.Close()
        if err != nil {
            fmt.Println("创建文件错误")
        }
        receivedBytes, err = io.Copy(f, connection)
        if err != nil {
            panic("传输错误")
        }

        fmt.Printf("传输完成。接收:%d \n", receivedBytes)
    }
}

io.Copy 只适用于一个文件,并且没有其他附加操作(我认为这是因为它不会清空队列)。如果不必要的话,我不想为每个文件重新连接。但是我不确定我能做些什么。

有人有任何现有的包或方法的建议可以帮助吗?或者有示例代码吗?或者我只是错了,用Go尝试这个是个坏主意?

我认为如果服务器能够在每次读取后刷新连接缓冲区,这可能已经足够了,这样就不会读取和/或复制额外的信息。

非常期待帮助,提前感谢。

编辑:更新的代码仍然无法工作。我认为问题可能是 bufio.Reader

func getFileFromClient(connection net.Conn) {
    var numberOfPics int
    var err error
    var receivedBytes int64
    var fileName string
    r := bufio.NewReader(connection)
    strNumberOfPics, err := r.ReadString('\n')
    if err != nil {
        fmt.Printf("读取错误:%s\n", err)
        return
    }
    strNumberOfPics = strings.Trim(strNumberOfPics, "\n")
    numberOfPics, err = strconv.Atoi(strNumberOfPics)
    if err != nil {
        fmt.Printf("Atoi错误:%s\n", err)
        panic("Atoi")
    }
    fmt.Printf("接收 %d 张图片:\n", numberOfPics)
    for i := 0; i < numberOfPics; i++ {
        // 获取文件名:
        fileName, err = r.ReadString('\n')
        if err != nil {
            fmt.Printf("接收错误:%s\n", err)
        }
        fileName = strings.Trim(fileName, "\n")
        fmt.Printf("文件名:%s\n", fileName)
        f, err := os.Create(fileName)
        defer f.Close()
        if err != nil {
            fmt.Println("创建文件错误")
        }
        // 获取文件大小
        strFileSize, err := r.ReadString('\n')
        if err != nil {
            fmt.Printf("读取大小错误 %s\n", err)
            panic("读取大小")
        }
        strFileSize = strings.Trim(strFileSize, "\n")
        fileSize, err := strconv.Atoi(strFileSize)
        if err != nil {
            fmt.Printf("大小Atoi错误:%s\n", err)
            panic("大小Atoi")
        }
        fmt.Printf("图片大小:%d\n", fileSize)

        receivedBytes, err = io.CopyN(f, connection, int64(fileSize))
        if err != nil {
            fmt.Printf("传输错误:%s\n", err)
            panic("传输错误")
        }
        fmt.Printf("传输完成。接收:%d \n", receivedBytes)
    }
}

编辑2:我没有让这个解决方案工作。我非常确定是因为我使用了 bufio。但是,我通过使用 io.Copy 传输单个 zip 文件使其工作。另一个可行的解决方案是通过使用 HTTP 传输 zip 文件。如果你在尝试类似的东西时遇到困难并需要帮助,请随时给我发送消息。感谢大家的帮助!

英文:

I am trying to write a program in go which has two parts. One part is the client who tries to upload multiple pictures to the other part the server.

The server side should do the following:

  1. Get the number of files which will be send
  2. Loop for every file
  3. Get filename
  4. Get the file and save it
  5. Go to 3

So far the server side is doing the following:

func getFileFromClient(connection net.Conn) {
var numberOfPics int
var err error
var receivedBytes int64
var fileName string
r := bufio.NewReader(connection)
strNumberOfPics, err := r.ReadString(&#39;\n&#39;)
if err != nil {
fmt.Printf(&quot;Error reading: %s\n&quot;, err)
return
}
fmt.Printf(&quot;Read: %s\n&quot;, strNumberOfPics)
strNumberOfPics = strings.Trim(strNumberOfPics, &quot;\n&quot;)
numberOfPics, err = strconv.Atoi(strNumberOfPics)
if err != nil {
fmt.Printf(&quot;Error Atoi: %s\n&quot;, err)
panic(&quot;Atoi&quot;)
}
fmt.Printf(&quot;Receiving %d pics:\n&quot;, numberOfPics)
for i := 0; i &lt; numberOfPics; i++ {
// Getting the file name:
fileName, err = r.ReadString(&#39;\n&#39;)
if err != nil {
fmt.Printf(&quot;Error receiving: %s\n&quot;, err)
}
fmt.Printf(&quot;Filename: %s\n&quot;, fileName)
fileName = strings.Trim(fileName, &quot;\n&quot;)
f, err := os.Create(fileName)
defer f.Close()
if err != nil {
fmt.Println(&quot;Error creating file&quot;)
}
receivedBytes, err = io.Copy(f, connection)
if err != nil {
panic(&quot;Transmission error&quot;)
}
fmt.Printf(&quot;Transmission finished. Received: %d \n&quot;, receivedBytes)
}
}

io.Copy is working for just one file and nothing additional (because it does not empty the queue I think). I do not want to reconnect every time for every file if I do not have too. But I am not sure what I actually can do about that.

Has anyone any suggestions of an existing package or method which could help? Or example code? Or am I just plain wrong and it is a bad idea to even try this with go?

I think it might be enough if the server is able to flush the connection buffer after every read so no additional info is read and/or copied.

Really looking forward for help, thanks in advance

EDIT: Updated Code still not working. I think it might be the bufio.reader

func getFileFromClient(connection net.Conn) {
var numberOfPics int
var err error
var receivedBytes int64
var fileName string
r := bufio.NewReader(connection)
strNumberOfPics, err := r.ReadString(&#39;\n&#39;)
if err != nil {
fmt.Printf(&quot;Error reading: %s\n&quot;, err)
return
}
strNumberOfPics = strings.Trim(strNumberOfPics, &quot;\n&quot;)
numberOfPics, err = strconv.Atoi(strNumberOfPics)
if err != nil {
fmt.Printf(&quot;Error Atoi: %s\n&quot;, err)
panic(&quot;Atoi&quot;)
}
fmt.Printf(&quot;Receiving %d pics:\n&quot;, numberOfPics)
for i := 0; i &lt; numberOfPics; i++ {
// Getting the file name:
fileName, err = r.ReadString(&#39;\n&#39;)
if err != nil {
fmt.Printf(&quot;Error receiving: %s\n&quot;, err)
}
fileName = strings.Trim(fileName, &quot;\n&quot;)
fmt.Printf(&quot;Filename: %s\n&quot;, fileName)
f, err := os.Create(fileName)
defer f.Close()
if err != nil {
fmt.Println(&quot;Error creating file&quot;)
}
// Get the file size
strFileSize, err := r.ReadString(&#39;\n&#39;)
if err != nil {
fmt.Printf(&quot;Read size error %s\n&quot;, err)
panic(&quot;Read size&quot;)
}
strFileSize = strings.Trim(strFileSize, &quot;\n&quot;)
fileSize, err := strconv.Atoi(strFileSize)
if err != nil {
fmt.Printf(&quot;Error size Atoi: %s\n&quot;, err)
panic(&quot;size Atoi&quot;)
}
fmt.Printf(&quot;Size of pic: %d\n&quot;, fileSize)
receivedBytes, err = io.CopyN(f, connection, int64(fileSize))
if err != nil {
fmt.Printf(&quot;Transmission error: %s\n&quot;, err)
panic(&quot;Transmission error&quot;)
}
fmt.Printf(&quot;Transmission finished. Received: %d \n&quot;, receivedBytes)
}
}

EDIT 2: I did not get this solution to work. I am pretty sure it is because I used bufio. I did however get it to work by transmitting a single zip file with io.copy. Another solution which worked was to transmit a zip file by using http. If you are stuck trying something similar and need help feel free to send me a message. Thanks to all of you for your help

答案1

得分: 1

保持你目前的实现,你遗漏的是io.Copy()会一直读取源文件直到找到EOF,所以它会一次性读取所有剩余的图像。

此外,客户端必须为每个图像发送其字节大小(你可以在发送名称后执行此操作)。
在服务器端,只需读取大小,然后使用io.CopyN()读取确切数量的字节。

编辑:实际上,你也可以像之前那样并行发送图像,而不是串行发送,这意味着你为每个文件传输打开一个新连接,然后无需发送图像数量或其大小即可读取整个文件。

如果你想要另一种选择,一个好的选择是使用传统的HTTP和多部分请求。内置模块mime/multipart允许你通过HTTP进行文件传输。当然,这意味着你需要重写你的程序。

英文:

Keeping your implementation so far, the thing you're missing is that io.Copy() reads from source until it finds an EOF, so it will read all the remaining images in one go.

Also, the client must send, for each image, its size in bytes (you could do that after sending the name).
In the server, just read the size and then use io.CopyN() to read that exact number of bytes.

EDIT: as a matter of fact, you could also do things like you were doing and send images in parallel instead of serially, that would mean you open a new connection for each file transfer and then read all of the file withouth needing to send the amount of images or their size.

In case you want an alternative, a good option would be using good 'ol HTTP and multipart requests. There's the built-in module mime/multipart that allows you to do file transfers over HTTP. Of course, that would mean you'd have to rewrite your program.

答案2

得分: 0

我的建议是将您想要传输的所有图像压缩成一个zip文件,然后将其作为单个multipart POST请求发送。这样,您就可以以标准的方式了解所有的验收标准。

您可以使用https://golang.org/pkg/archive/zip/轻松地压缩多个文件。

英文:

My suggestion is to zip all the images you want to transfer and then send them as a single multipart POST request. In that way you have a standard way of knowing all your Acceptance criteria.

You can easily zip multiple files using https://golang.org/pkg/archive/zip/

huangapple
  • 本文由 发表于 2015年12月16日 00:01:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/34293809.html
匿名

发表评论

匿名网友

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

确定