doutandusegang2961
doutandusegang2961
采纳率0%
2017-02-15 22:37 阅读 23
已采纳

通过http POST发送os.Stdin而不将文件加载到内存中

I am trying to send a file to a server via a POST request. To accomplish this, I use the following code:

func newfileUploadRequest(uri string) (*http.Request, error) {

    body := new(bytes.Buffer)
    writer := multipart.NewWriter(body)
    part, err := writer.CreateFormFile("file", "file")
    if err != nil {
        return nil, err
    }
    io.Copy(part, os.Stdin)

    err = writer.Close()
    if err != nil {
        return nil, err
    }

    request, err := http.NewRequest("POST", uri, body)
    if err != nil {
        return nil, err
    }
    request.Header.Set("Content-Type", writer.FormDataContentType())
    return request, nil
}


func main() {
    r, err := newfileUploadRequest("http://localhost:8080/")
    if err != nil {
        panic(err)
    }

    client := &http.Client{}
    resp, err := client.Do(r)
    if err != nil {
        panic(err)
    }
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }
    print(string(body))
}

While this works well, it is my understanding that io.Copy will copy the entire file into memory before the POST request is sent. Large files (multiple GB) will create issues. Is there a way to prevent this? I found this, but that simply says to use io.Copy.

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享

2条回答 默认 最新

  • 已采纳
    dougutuo9879 dougutuo9879 2017-02-15 23:19

    You can avoid copying the data in memory by using an io.Pipe and a goroutine that copies from the file to the pipe:

    func newfileUploadRequest(uri string) (*http.Request, error) {
      r, w := io.Pipe()
      writer := multipart.NewWriter(w)
      go func() {
        part, err := writer.CreateFormFile("file", "file")
        if err != nil {
            w.CloseWithError(err)
            return
        }
        _, err = io.Copy(part, os.Stdin)
        if err != nil {
            w.CloseWithError(err)
            return
        }
        err = writer.Close()
        if err != nil {
            w.CloseWithError(err)
            return
        }
      }()
    
      request, err := http.NewRequest("POST", uri, r)
      if err != nil {
        return nil, err
      }
      request.Header.Set("Content-Type", writer.FormDataContentType())
      return request, nil
    }
    
    点赞 评论 复制链接分享
  • dongyan1993 dongyan1993 2017-02-15 23:19

    Use io.Pipe() - https://golang.org/pkg/io/#Pipe

    You will need to use a goroutine but it isn't too hard to do. If you need a specific example leave a comment and I'll get one for you.

    点赞 评论 复制链接分享

相关推荐