如何使用Golang实现HTTP文件上传与下载_Golang文件上传与下载功能实现

4次阅读

go 标准库 http 包原生支持文件上传下载;需先调用 req.ParseMultipartForm(32

如何使用Golang实现HTTP文件上传与下载_Golang文件上传与下载功能实现

Go 标准库http 包完全支持文件上传与下载,无需第三方依赖;关键在于正确处理 multipart/form-data 请求体和设置响应头。

req.ParseMultipartForm 解析上传文件

浏览器表单提交文件时,Content-Typemultipart/form-data,必须先调用 ParseMultipartForm 才能访问 req.MultipartForm。否则 req.FormFile 会返回 http.ErrNotMultipart

常见错误是忽略参数:该方法第一个参数是最大内存缓存字节数(如 32 表示 32MB),超过部分会写入临时磁盘文件。设为 0 会导致解析失败。

  • 推荐设为合理上限,例如 req.ParseMultipartForm(32
  • 调用后通过 req.FormFile("file") 获取 *multipart.FileHeader
  • header.Filename 是客户端原始文件名,不可直接拼路径,需校验或重命名
  • file, err := header.Open() 得到可读流,再用 io.Copy 写入本地文件

http.ServeFile 或手动 io.Copy 实现安全下载

http.ServeFile 简单但危险:它会自动解析 URL 路径,若用户传 ../../etc/passwd 可能触发目录穿越。生产环境应避免直接使用。

立即学习go语言免费学习笔记(深入)”;

更稳妥的方式是:固定文件根目录,用 filepath.Join 拼接并校验路径是否在允许范围内。

  • filepath.Clean 规范路径,再用 strings.HasPrefix 检查是否仍在白名单目录内
  • 设置响应头:w.Header().Set("Content-Disposition", "attachment; filename=""+filename+""")
  • 设置 Content-Type:未知类型可用 mime.TypeByExtension(filepath.Ext(filename))
  • 最后用 io.Copy(w, file) 流式传输,避免大文件内存溢出

上传时注意 MaxBytesReader 防止请求体过大

即使设置了 ParseMultipartForm 的上限,攻击者仍可能发送超长请求头或恶意构造的 multipart 边界,导致内存耗尽。应在 handler 开头加一层限制。

  • http.MaxBytesReader(w, r, maxUploadSize) 包裹原始 *http.Request.Body
  • 例如 maxUploadSize = 100 (100MB),超出则返回 413 Payload Too Large
  • 这个限制在 ParseMultipartForm 之前生效,是第一道防线
  • 注意:该包装器只限制整个请求体大小,不区分文件字段和其他表单字段

下载时别漏掉 Content-Length 和缓冲策略

缺失 Content-Length 会导致某些客户端(如 curl、wget)无法显示进度,浏览器也可能无法正确识别文件大小。虽然 io.Copy 能工作,但显式设置长度更规范。

  • stat, _ := file.Stat() 获取文件大小,然后 w.Header().Set("Content-Length", strconv.FormatInt(stat.Size(), 10))
  • 对于超大文件(>1GB),考虑用 http.ServeContent 替代 io.Copy,它支持范围请求(Range)、ETag 和条件 GET
  • 不要用 ioutil.ReadAll 读取整个文件进内存——这是最常被忽略的性能陷阱

文件上传下载看似简单,但边界校验、路径安全、流控和响应头细节稍有疏忽就可能引发漏洞或服务异常。尤其是 filepath.Join + filepath.Clean 组合做路径白名单,以及 MaxBytesReader 的前置保护,这两处最容易被跳过。

text=ZqhQzanResources