liveBook · Manning

MultipartReader

As explained in Go in Practice, Second Edition

MultipartReader is a Go HTTP request method that returns a reader for incrementally consuming the parts of a multipart request body.

MultipartReader returns a reader for consuming a multipart HTTP request body as it arrives. It provides lower-level access than ParseMultipartForm, allowing handlers to process files and fields incrementally instead of caching the completed upload locally. section 10.2.6

Use MultipartReader when uploads are large, numerous, or destined for another location:

  • Call r.MultipartReader() and handle its error before processing the request. section 10.2.6
  • Loop over mr.NextPart(), stopping at io.EOF, and inspect each part with FormName(). section 10.2.6
  • Save or process each file or text field chunk by chunk, avoiding unnecessary local caching. section 10.2.6
Listing 10.14 HTML form containing a file and text field
<!doctype html>
<html>
  <head>
    <title>File Upload</title>
  </head>
  <body>
    <form action="/" method="POST" enctype="multipart/form-data">
      <label for="name">Name:</label>              #1
      <input type="text" name="name" id="name">    #1
      <br>
      <label for="file">File:</label>             #2
      <input type="file" name="file" id="file">   #2
      <br>
      <button type="submit" name="submit">Submit</button>    #3
    </form>
  </body>
</html>

Use the multipart/form-data form to see the file and text fields that produce the request stream. 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")
     }
}

Follow the handler implementation from reader creation through part iteration and incremental saving. section 10.2.6

How Go in Practice, Second Edition covers it

How deep it goes
Explains reader creation, part iteration, end-of-stream handling, and field inspection, but does not show complete part-saving logic.
Examples
Uses an HTML upload form and a Go handler that iterates over parts while saving uploaded files incrementally.
What it assumes
Assumes basic Go HTTP handler knowledge and multipart forms while contrasting MultipartReader with ParseMultipartForm.
What you can do after
Builds a handler that reads multipart fields incrementally and forwards or stores upload data without waiting for completion.
How it connects
Connects multipart forms with HTTP requests, ParseMultipartForm, multipart.Reader, request parts, and incremental file handling.

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.