如何限制文件上传的大小

yashiro1899 · · 1118 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

```go err := r.ParseMultipartForm(32 << 20) // 32Mb if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) } ``` 文件上传一般会采用 POST `multipart/form-data` 的形式,处理这类请求要调用 `r.ParseMultipartForm`,无论是显式调用,还是在 `r.FormFile` 里面的隐式调用。 那 32Mb 是对文件上传大小的限制吗?不是,上传的文件们按顺序存入内存中,累加大小不得超出 32Mb ,最后累加超出的文件就存入系统的临时文件中。非文件字段部分不计入累加。所以这种情况,文件上传是没有任何限制的。 ```go r.Body = http.MaxBytesReader(w, r.Body, 32<<20+512) ... ``` 通过上面代码,可以把 POST Body 整体限制在 32.5Mb,否则就会返回 `http: request body too large` 的错误。从而可以防止一些恶意的大文件上传。 但如果想做到更精细的控制,比如:文件大小的限制、文件类型的限制等,`r.ParseMultipartForm` 就无能为力了。我打算做如下校验: * 文件类型校验 * 文件大小校验 * 字段白名单 * 一旦校验失败,立即停止解析 ```go var filesMax int64 = 4 << 20 var valuesMax int64 = 512 // 整体限制 4.5Mb r.Body = http.MaxBytesReader(w, r.Body, filesMax+valuesMax) reader, err := r.MultipartReader() if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } // 白名单 files := map[string][]byte{"file": nil} values := map[string]string{"text": ""} for { part, err := reader.NextPart() if err == io.EOF { break } if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } var buf bytes.Buffer filename := part.FileName() name := part.FormName() // 非文件字段 if filename == "" { if _, ok := values[name]; !ok { http.Error(w, name+" is not expected", http.StatusBadRequest) return } n, err := io.CopyN(&buf, part, valuesMax+1) if err != nil && err != io.EOF { http.Error(w, err.Error(), http.StatusInternalServerError) return } valuesMax -= n if valuesMax < 0 { http.Error(w, "multipart: message too large", http.StatusBadRequest) return } values[name] = buf.String() continue } // 文件字段 if _, ok := files[name]; !ok { http.Error(w, name+" is not expected", http.StatusBadRequest) return } n, err := io.CopyN(&buf, part, filesMax+1) if err != nil && err != io.EOF { http.Error(w, err.Error(), http.StatusInternalServerError) return } filesMax -= n if filesMax < 0 { http.Error(w, "file size over limit", http.StatusBadRequest) return } files[name] = buf.Bytes() contentType := http.DetectContentType(files[name]) if contentType != "application/zip" { http.Error(w, "file type not allowed", http.StatusBadRequest) return } } ``` 上面代码主要做了这样几个事: * 只处理 `text` 字段和 `file` 文件字段 * 限定文件大小 4Mb、限定普通字段 512b * 限定文件类型为 `application/zip` * 一旦有校验错误立即返回错误

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1118 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传