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
