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
