Demo
transport := &http.Transport{
DisableKeepAlives: true,
}
if proxy != "" {
proxyUrl, _ := nUrl.Parse(proxy)
proxyFunc := http.ProxyURL(proxyUrl)
transport.Proxy = proxyFunc
}
client := &http.Client{Transport: transport}
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
//设置非文件入参k-v格式
for key, val := range params {
err0 := bodyWriter.WriteField(key, fmt.Sprint(val))
if err0 != nil {
return
}
}
//设置文件入参
for key, file := range fileparams {
fileWriter, err0 := bodyWriter.CreateFormFile(key, file.Filename)
// 把文件流写入到缓冲区里去
written, err0 := io.Copy(fileWriter, file.File)
if err0 != nil {
return
}
}
// 一定要记着关闭
err0 := bodyWriter.Close()
if err0 != nil {
return
}
//创建请求
req, err0 := http.NewRequest(http.MethodPost, meta.url, bodyBuf)
if err != nil {
return
}
//添加头
for key, val := range headers {
req.Header.Set(key, val)
}
req.Header.Set("Content-Type", bodyWriter.FormDataContentType())
// 发送post请求
response, err0 := client.Do(req)
if err0 != nil {
return
}
defer response.Body.Close()
//处理响应
body, err0 := ioutil.ReadAll(response.Body)
if err0 != nil {
return
}
...
注意事项
- golang 官方函数CreateFormFile中 会自动设置文件字段的Content-Type,如果请求的接口对Content-Type有要求,需要注意此函数是否适用,如果不适用可自定义一个方法实现
// CreateFormFile is a convenience wrapper around CreatePart. It creates
// a new form-data header with the provided field name and file name.
func (w *Writer) CreateFormFile(fieldname, filename string) (io.Writer, error) {
h := make(textproto.MIMEHeader)
h.Set("Content-Disposition",
fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
escapeQuotes(fieldname), escapeQuotes(filename)))
h.Set("Content-Type", "application/octet-stream")
return w.CreatePart(h)
}
-
、 ,文件使用此方法读取完成后,offset在文件末尾,如果想再次读取文件内容,需要先使用Seek函数重置偏移位为文件初始位置,再进行读取;否则 再次读到的文件内容为空!
比如有个应用场景是先计算文件的md5,再将文件、md5作为入参请求http post接口。
hash := md5.New()
if _, err := io.Copy(hash, file); err != nil {
return 0
}
md5sum := hex.EncodeToString(hash.Sum(nil))
//!!!一定要重置
file.Seek(0, io.SeekStart)
...
fileWriter, err0 := bodyWriter.CreateFormFile(key, file.Filename)
written, err0 := io.Copy(fileWriter, file.File)
...
- 设置Content-Type
req.Header.Set("Content-Type", bodyWriter.FormDataContentType())
// FormDataContentType returns the Content-Type for an HTTP
// multipart/form-data with this Writer's Boundary.
func (w *Writer) FormDataContentType() string {
b := w.boundary
// We must quote the boundary if it contains any of the
// tspecials characters defined by RFC 2045, or space.
if strings.ContainsAny(b, `()<>@,;:\"/[]?= `) {
b = `"` + b + `"`
}
return "multipart/form-data; boundary=" + b
}