liveBook · Manning

Multipart request

As explained in Go in Practice, Second Edition

A multipart message is a message divided into separately typed parts, each with its own headers and content.

A multipart request carries different kinds of content in separate parts. A file-upload form, for example, can submit text fields alongside a binary file with its own content type and metadata. Set the HTML form’s encoding to `multipart/form-data` when uploading a file. section 10.2.3 section 10.2.1

Listing 10.10 A form with a single-value file-upload field
<!doctype html>
<html>
  <head>
    <title>File Upload</title>
  </head>
  <body>
    <form action="/" method="POST" enctype="multipart/form-data">    #1
      <label for="file">File:</label>                  #2
      <input type="file" name="file" id="file">       
      <br>
      <button type="submit" name="submit">Submit</button>   #3
    </form>
  </body>
</html>

A POST form with `enctype="multipart/form-data"` and a file input provides a minimal upload request. section 10.2.3

In a Go HTTP handler, `ParseForm` handles text fields; use `ParseMultipartForm` when the form includes files. For a straightforward single-file upload, `r.FormFile("file")` provides the file and its metadata. Close the file when finished, and copy its contents to the destination. section 10.2.1 section 10.2.3

Listing 10.11 Handling a single file upload
func fileForm(w http.ResponseWriter, r *http.Request) {    #1
     if r.Method == "GET" {                               #2
            t, _ := template.ParseFiles("file.html")     
            t.Execute(w, nil)                            
     } else {

            f, h, err := r.FormFile("file")    #3
            if err != nil {         #4
                   panic(err)       #4
            }                       #4
            defer f.Close()                       #5
            filename := "/tmp/" + h.Filename     #6
            out, err := os.Create(filename)    #7
            if err != nil {
                   panic(err)
            }
            defer out.Close()     #8

            io.Copy(out, f)         #9
            fmt.Fprint(w, "Upload complete")
     }
}

The handler retrieves the uploaded file with `FormFile` and copies it to a local file. section 10.2.3

For a large upload, or when the server passes data to another destination, process the request incrementally instead of waiting to parse the whole form. Call `r.MultipartReader()`, then iterate through parts with `NextPart()` as data arrives. This avoids using local storage as an unnecessary cache for large or concurrent uploads. section 10.2.6

Listing 10.15 Saving uploaded files incrementally
func fileForm(w http.ResponseWriter, r *http.Request) {   #1
     if r.Method == "GET" {                                #2
            t, _ := 
            template.ParseFiles("file_plus.html")         
            t.Execute(w, nil)                             
     } else {
            mr, err := r.MultipartReader()               #3
            if err != nil {                             
                   panic("Failed to read multipart 
                   message")                            
            }                                           





            values := make(map[string][]string)   #4
            maxValueBytes := int64(10 << 20)     #5
            for {   #6
                   part, err := mr.NextPart()   #7
                   if err == io.EOF {           #7
                           break                #7
                   }                            #7
                   name := part.FormName()   #8
                   if name == "" {           #8
                           continue          #8
                   }                         #8
                   filename := part.FileName()   #9
                   var b bytes.Buffer     #10
                   if filename == "" {    #11
                           n, err := io.CopyN(&b, part, 
                           maxValueBytes)   #12
                           if err != nil && err != 
                           io.EOF {                        #13
                                  fmt.Fprint(w, "Error
                                   processing form")   
                                  return               
                           }                           
                           maxValueBytes -= n           #14
                           if maxValueBytes == 0 {     
                                  msg := "multipart 
                                  message too large"     
                                  fmt.Fprint(w, msg)   
                                  return               
                          }                            
                          values[name] = append(values
                          [name],b.String())   #15
                          continue             #15
                   }
                   dst, err := os.Create("/tmp/" + 
                   filename)                                  #16
                   defer dst.Close()                   #17
                   if err != nil {
                          return
                   }
                   for { #                                 #18
                           buffer := make([]byte, 100000) 
                           cBytes, err := 
                           part.Read(buffer)              
                           if err == io.EOF {             
                                  break                   
                           }                              
                           dst.Write(buffer[0:cBytes])    
                   }                                      
            }
            fmt.Fprint(w, "Upload complete")
     }
}

The incremental handler obtains a multipart reader and begins a loop over incoming parts. section 10.2.6

How Go in Practice, Second Edition covers it

How deep it goes
Works through form parsing, single-file handling, and incremental part iteration without detailing multipart wire-format internals.
Examples
Uses a multipart HTML upload form, a Go single-file handler, and an incremental upload handler.
What it assumes
Assumes familiarity with HTML forms and Go HTTP handlers while clarifying when text-form parsing is insufficient.
What you can do after
Apply it via Go’s `http.Request` methods to receive files or process multipart parts incrementally.
How it connects
Connects HTML forms, MIME content types, file uploads, HTTP handlers, and streaming.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Go in Practice, Second Edition. The section links point to the parts of the book it draws on.