golang使用MultipartForm分析http request内容
在使用curl给http server发请求的时候,可以使用-F参数指定这是一个multipart的请求;这种请求可以同时包含文本的form数据,也可以包含二进制文件数据。
下面这段代码是server端分析http请求解析multipart的例子:
func httpHandle(w http.ResponseWriter, r *http.Request) {
// http.Request has a member MultipartForm, it's defined as:
// MultipartForm *multipart.Form
// type Form struct {
// Value map[string][]string
// File map[string][]*FileHeader
// }
err := r.ParseMultipartForm(1048576)
if err != nil {
log.Printf("Cannot ParseMultipartForm, error: %v\n", err)
return
}
if r.MultipartForm == nil {
log.Printf("MultipartForm is null\n")
return
}
if r.MultipartForm.Value != nil {
parseMultipartFormValue(r.MultipartForm.Value)
}
if r.MultipartForm.File != nil {
parseMultipartFormFile(r, r.MultipartForm.File)
}
}
// parse form data
func parseMultipartFormValue(formValues map[string][]string) {
for formName, values := range formValues {
log.Printf("Value formname: %s\n", formName)
for i, value := range values {
log.Printf(" formdata[%d]: content=[%s]\n", i, value)
//m := make(map[string]string)
//_ = json.NewDecoder(strings.NewReader(value)).Decode(&m)
//log.Printf(" formdata[%d]: json=[%v]\n", i, value)
}
}
return
}
// parse form file
func parseMultipartFormFile(r *http.Request, formFiles map[string][]*multipart.FileHeader) {
for formName, _ := range formFiles {
// func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error)
// FormFile returns the first file for the provided form key
formFile, formFileHeader, _ := r.FormFile(formName)
log.Printf("File formname: %s, filename: %s, file length: %d\n", formName, formFileHeader.Filename, formFileHeader.Size)
if strings.HasSuffix(formFileHeader.Filename, ".zip") {
zipReader, _ := zip.NewReader(formFile, formFileHeader.Size)
for i, zipMember := range zipReader.File {
f, _ := zipMember.Open()
defer f.Close()
buf, _ := ioutil.ReadAll(f)
log.Printf(" formfile[%d]: filename=[%s], size=%d, content=[%s]\n", i, zipMember.Name, len(buf), strings.TrimSuffix(string(buf), "\n"))
}
} else {
var b bytes.Buffer
_, _ = io.Copy(&b, formFile)
log.Printf(" formfile: content=[%s]\n", strings.TrimSuffix(b.String(), "\n"))
}
}
}
客户端调用:
$ curl -X POST -H "Content-Type: multipart/form-data" \
-F 'payload={"aa":"bb"}' \
-F 'file1=@a1.txt' \
-F 'file2=@a3.zip' \
http://localhost:8080/multipart
举个例子:
2020/04/25 16:24:15 Value formname: payload
2020/04/25 16:24:15 formdata[0]: content=[{"aa":"bb"}]
2020/04/25 16:24:15 File formname: file1, filename: a1.txt, file length: 3
2020/04/25 16:24:15 formfile: content=[a1]
2020/04/25 16:24:15 File formname: file2, filename: a3.zip, file length: 322
2020/04/25 16:24:15 formfile[0]: filename=[a31.txt], size=10, content=[a31a31a31]
2020/04/25 16:24:15 formfile[1]: filename=[a32.txt], size=10, content=[a32a32a32]
这个例子我们看到server端如何解析formdata,如果是json串还可以解析成json对象;以及如何解析上传文件的内容,并且如果是zip文件,还如何读取zip的每一个member。
另外要注意的是,不管是formdata还是formfile,一个form项可对应多个数据;例如:
$ curl -X POST -H "Content-Type: multipart/form-data" \
-F 'payload={"aa":"bb"}' \
-F 'payload={"cc":"dd"}' \
-F 'file1=@a1.txt' \
-F 'file1=@a3.zip' \
http://localhost:8080/multipart
这里form payload包含两块内容,form file1也包含两个文件,我不知道为什么要这样设计,为什么要用同一个formname对应多个内容呢,分开不是更好吗?
在实际应用中,我们一般都分开,即不使用一个formname对应多个值,所以request.FormFile()函数只取第一个File对象已经够用了。
有疑问加站长微信联系(非本文作者)