GO 文件上传操作

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
}

  • 文件handlermultipart.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、成功结果

浏览器端显示如下消息。
在这里插入图片描述

发布了12 篇原创文章 · 获赞 3 · 访问量 6013

猜你喜欢

转载自blog.csdn.net/qq_40185499/article/details/104124046