表单上传
为保证表单上传文件成功,需要添加表单form的enctype属性,enctype属性有三种:
application/x-www-form-urlencoded 表示在发送前编码所有字符(默认)
multipart/form-data 表示不对字符编码(表单上传时必须使用这个)
text/plain 空格转换为”+”加号,但不对特殊字符编码
一、服务端处理
func upload(w http.ResponseWriter, r *http.Request) {
r.ParseMultipartForm(32 << 20) // maxMemory
file, handler, err := r.FormFile(“uploadfile”)
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
fmt.Fprintf(w, “%v”, handler.Header)
f, err := os.OpenFile(“./test/”+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Println(err)
return
}
io.Copy(f, file)
}
处理文件上传我们需要调用r.ParseMultipartForm,里面的参数表示maxMemory,调用ParseMultipartForm之后,上传的文件存储在maxMemory大小的内存里面,如果文件大小超过了maxMemory,那么剩下的部分将存储在系统的临时文件中。
通过r.FormFile获取上面的文件句柄,然后实例中使用了io.Copy来存储文件。
上传文件主要的三步处理
1、表单中增加enctype=”multipart/form-data”;
2、服务端调用r.ParseMultipartForm,把上传的文件存储在内存和临时文件中;
3、使用r.FormFile获取文件句柄,然后对文件进行存储等处理。
FileHander数据结构
文件handler是multipart.FileHeader,结构信息如下:
type FileHeader struct {
Filename string
Header textproto.MIMEHeader
// contains filtered or unexported fields
}
比如:
map[Content-Type:[application/pdf] Content-Disposition:[form-data; name=”file”; filename=”coffee.pdf”]]
二、客户端处理
package main
import (
“bytes”
“fmt”
“io”
“io/ioutil”
“mime/multipart”
“net/http””os”
)
func postFile(filename string, targetUrl string) error {
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
// 读取文件到缓存
fileWriter, err := bodyWriter.CreateFormFile(“uploadfile”, filename)
if err != nil {
fmt.Println(“err writing to buffer”)
return err
}
// 打开句柄
fh, err := os.Open(filename)
if err != nil {
fmt.Println(“error opening file”)
return err
}
defer fh.Close()
// io copy
_, err = io.Copy(fileWriter, fh)
if err != nil {
fmt.Println(“error copy file”)
return err
}
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
resp, err := http.Post(targetUrl, contentType, bodyBuf)
if err != nil {
return err
}
defer resp.Body.Close()
resp_body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(“error ioread file”)
return err
}
fmt.Println(resp.Status)
fmt.Println(string(resp_body))
return nil
}
func main() {
target_url := “http://localhost:9090/upload”
filename := “coffee.pdf”
postFile(filename, target_url)
}
客户端通过multipart.Write把文件的文本流写入一个缓存中,然后调用http的Post方法把缓存传到服务器。