Dealing with multipart HTTP data

Dealing with multipart HTTP data

When reading HTTP multipart data, using Request.FormFile to get files from the request may not be the best way. It causes the http package read the body, and store data in temporary files if data is larger than 10MB. To prevent that, you have to use the multipart package and read the body yourself.

Multipart body is separated by a boundary string, which can be obtained from the Content-Type:

1
2
m, p, _:=mime.ParseMediaType(req.Header.Get("Content-Type"))
boundary:=p["boundary"]

Once the boundary is known, you can create a multipart reader:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
reader:=multipart.NewReader(req.Body,boundary)
for  {
  part, err:=reader.NextPart()
  if err==io.EOF {
    // Done reading body
    break
  }
  contentType:=part.Header.Get("Content-Type")
  fname:=part.FileName()
  // part is an io.Reader, deal with it
}

Creating multipart payload is a bit more involved. First, create a multipart writer:

1
2
body := bytes.Buffer{}
writer := multipart.NewWriter(&body)

Then, create and add the parts:

1
2
3
4
hdr := textproto.MIMEHeader{}
hdr.Set("Content-Type", "application/json")
part, _ := writer.CreatePart(hdr)
part.Write(jsonData)

Build the request:

1
2
3
writer.Close()
request, _ := http.NewRequest(http.MethodPost, url, &body)
request.Header.Set("Content-Type", fmt.Sprintf("multipart/mixed;boundary=%s", writer.Boundary()))

If you need to stream the files to the request, then you need to create a pipe, and build the multipart content using a separate goroutine.