liveBook · Manning

File upload

As explained in Go in Practice, Second Edition

A file upload is the transfer of a local file from a client to a server through a form or other network request.

A file upload transfers a local file to a Go web server through a multipart form. The request combines binary file data with metadata such as the filename and may also contain ordinary text fields. section 10.0 section 10.2.3

A basic form uses POST, enctype="multipart/form-data", and a file input. The handler retrieves the upload with Request.FormFile, closes the returned file, creates a destination, and copies the data with io.Copy. section 10.2.3

For large or concurrent uploads, avoid ParseMultipartForm when temporary server storage would become a bottleneck. Request.MultipartReader exposes the incoming stream so the handler can process each part incrementally and pass data to its final destination. section 10.2.6

Single-file upload

A form submits a selected file to the root endpoint; the handler reads the file and metadata with r.FormFile("file"), creates /tmp/<filename>, and copies the upload into it.

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

Inspect the incremental multipart loop that reads parts as they arrive instead of caching a complete upload first. section 10.2.6

How Go in Practice, Second Edition covers it

How deep it goes
Explains form configuration, single-file persistence, and incremental streaming, but stops short of production validation and storage policy.
Examples
Uses single-file HTML forms, Go handlers, mixed text-and-file submissions, and incremental multipart saving.
What you can do after
Apply it via Go's net/http and multipart APIs to save ordinary uploads or stream large files incrementally.
How it connects
Connects multipart form data with HTTP requests, HTML forms, file metadata, temporary files, and streaming I/O.

Also explained in

Compare how 5 books explain file upload →

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.