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

时间:2017-02-15 22:37:40

标签: go http-post stdin

我正在尝试通过POST请求将文件发送到服务器。为此,我使用以下代码:

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))
}

虽然这很有效,但据我了解,io.Copy会在发送POST请求之前将整个文件复制到内存中。大文件(多GB)会产生问题。有办法防止这种情况吗?我找到了this,但这只是说使用io.Copy。

2 个答案:

答案 0 :(得分:2)

您可以使用io.Pipe和从文件复制到管道的goroutine来避免复制内存中的数据:

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
}

答案 1 :(得分:1)

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

你需要使用goroutine,但这并不难。如果您需要一个具体的例子,请留下评论,我会为您准备一个。