本文实例为大家分享了GO实现文件上传操作的具体代码,供大家参考,具体内容如下
由于需求中有文件上传这一个需求,在这里我们就学习一下go语言如何上传文件。本文主要通过表单的方式进行文件上传操作。主要有以下三步:
表单中增加enctype
属性
服务端调用r.ParseMultipartForm
,把上传的文件存储在内存和临时文件中
使用r.FormFile
获取文件句柄,然后对文件进行存储等处理。
1、表单操作
要使表单能够上传文件,首先第一步就要添加form的enctype
属性进去,enctype
属性有如下三种情况:
application/x-www-form-urlencoded 表示在发送前编码所有字符(默认)
multipart/form-data 不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。
text/plain 空格转换为 "+" 加号,但不对特殊字符编码。
所以可以创建如下上传表单:
<html>
<head>
<title>上传文件</title>
</head>
<body>
<form enctype="multipart/form-data" action="/upload" method="post">
<input type="file" name="uploadfile" />
<input type="hidden" name="token" value="{{.}}"/>
<input type="submit" value="upload" />
</form>
</body>
</html>
2、服务端操作
在服务端只需要添加一个handlerFunc
并完善相关功能即可:
// 处理/upload 逻辑
func upload(w http.ResponseWriter, r *http.Request) {
//获取请求的方法
fmt.Println("method:", r.Method)
//GET的处理操作
if r.Method == "GET" {
crutime := time.Now().Unix()
h := md5.New()
io.WriteString(h, strconv.FormatInt(crutime, 10))
token := fmt.Sprintf("%x", h.Sum(nil))
t, _ := template.ParseFiles("upload.gtpl")
t.Execute(w, token)
} else {
//设置内存大小
r.ParseMultipartForm(32 << 20)
//获取上传文件
file, handler, err := r.FormFile("uploadfile")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
fmt.Fprintf(w, "%v", handler.Header)
//创建上传目录
os.Mkdir("./test", os.ModePerm)
//创建上传文件
f, err := os.Create("./test/" + handler.Filename)
//f, err := os.OpenFile("./test/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666) // 此处假设当前目录下已存在test目录
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
io.Copy(f, file)
}
}
在main()
函数中记得添加http.HandleFunc("/upload", upload)
即可。
通过http://127.0.0.1:9999/upload来测试文件上传。 截图
选择文件之后就会在当前目录下的test文件夹中成功上传文件。
3、流程解析
通过上面的代码可以看到,处理文件上传我们需要调用r.ParseMultipartForm
,里面的参数表示maxMemory
,调用ParseMultipartForm
之后,上传的文件存储在maxMemory
大小的内存里面,如果文件大小超过了maxMemory
,那么剩下的部分将存储在系统的临时文件中。我们可以通过r.FormFile
获取上面的文件句柄,然后实例中使用了io.Copy
来存储文件。我们可以尝试使用看一下使用的相关原函数:
ParseMultipartForm
函数如下:
func (r *Request) ParseMultipartForm(maxMemory int64) error {
if r.MultipartForm == multipartByReader {
return errors.New("http: multipart handled by MultipartReader")
}
if r.Form == nil {
err := r.ParseForm()
if err != nil {
return err
}
}
if r.MultipartForm != nil {
return nil
}
mr, err := r.multipartReader(false)
if err != nil {
return err
}
f, err := mr.ReadForm(maxMemory)
if err != nil {
return err
}
if r.PostForm == nil {
r.PostForm = make(url.Values)
}
for k, v := range f.Value {
r.Form[k] = append(r.Form[k], v...)
// r.PostForm should also be populated. See Issue 9305.
r.PostForm[k] = append(r.PostForm[k], v...)
}
r.MultipartForm = f
return nil
}
FormFile
函数如下:
func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
if r.MultipartForm == multipartByReader {
return nil, nil, errors.New("http: multipart handled by MultipartReader")
}
if r.MultipartForm == nil {
err := r.ParseMultipartForm(defaultMaxMemory)
if err != nil {
return nil, nil, err
}
}
if r.MultipartForm != nil && r.MultipartForm.File != nil {
if fhs := r.MultipartForm.File[key]; len(fhs) > 0 {
f, err := fhs[0].Open()
return f, fhs[0], err
}
}
return nil, nil, ErrMissingFile
}
文件handler
是multipart.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
}
4、成功结果
浏览器端显示如下消息。