背景
之前都是做桌面软件和嵌入式软件开发的。最近刚入门后端开发,对很多后端的知识不太了解。项目中有这样一个需求,需要向后端发送一组信息,其中包括文件资料,如图片。其他都是这些资料的元数据信息。如下图所示。请求发送后,后端service怎样从请求中获知文件名信息?
当然,我可以在发送情况的时候,在元数据里添加文件名字段,这也是一个办法。但从http请求中能否获得呢?我们一起来看看。
开始探索
经同事指引,了解到HTTP header中有 Content-Dispostion字段,它就是用来放置这些信息的。
在参考1中有详细的介绍。
Content-Disposition: form-data
Content-Disposition: form-data; name="fieldName"
Content-Disposition: form-data; name="fieldName"; filename="filename.jpg"
资料阅读过后,心中就有了答案了。上传的文件名必定藏在http请求中。经过研究请求的源码,发现在golang的HTTP Request中有下面这样的字段。
PostForm url.Values
// MultipartForm is the parsed multipart form, including file uploads.
// This field is only available after ParseMultipartForm is called.
// The HTTP client ignores MultipartForm and uses Body instead.
MultipartForm *multipart.Form
而Form字段的定义是:
type Form struct {
Value map[string][]string
File map[string][]*FileHeader
}
其中发现有一个指向FileHeader类型的指针数组,FileHeader看上去有点意思,里面有没有我们想要的信息呢?继续往下看。
// A FileHeader describes a file part of a multipart request.
type FileHeader struct {
Filename string
Header textproto.MIMEHeader
Size int64
content []byte
tmpfile string
}
果然,里面有Filename字段。而Filename正是我们想要的信息。那如何去获取呢?File是一个map表,它的值是一个指向FileHeader类型的指针数组,我们需要根据我们先遍历map,从map中取出一个指针数组,然后去遍历这个指针数组。
思路有了,那接下来就是验证的时刻了。
验证阶段
根据上面的思路,我们先写如下代码,从map中取出key(icon)对于的value,看看value内存的是什么。
fileMap := params.HTTPRequest.MultipartForm.File
for k, v := range fileMap {
fmt.Printf("key:%s, value:%s", k, v)
}
得到的结果如下:
key:icon, value:[%!s(*multipart.FileHeader=&{QQ音乐.png map[Content-Disposition:[form-data; name="icon"; filename="QQ音乐.png"] Content-Type:[image/png]]
有点意思了,从这个输出结果中,我们看到了前面说的Content-Disposition信息了,其中就有filename。这没让我失望。
继续遍历指针数组:
for _, files := range fileMap {
for _, file := range files {
fmt.Printf("filename:%s", file.Filename)
}
}
log输出结果如下,果然没让我们失望。
filename:QQ音乐.png
至此,问题完美解决!
参考
- Content-Disposition介绍
- go/src/net/http/request.go
- go/src/mime/multipart/formdata.go