文件已经上传到服务器翻译,服务器接受上传的优化 翻译+源码分析

一般的做法

err := r.ParseMultipartForm(32 << 20) // 32Mb

if err != nil {

http.Error(w, err.Error(), http.StatusBadRequest)

}

问题

请注意,32Mb是分配给请求体的字节存储在内存中,而不是请求体的限制,当满(33Mb)时,它将写入临时目录。

查看源码

src/net/http/request.go中

func (r *Request) ParseMultipartForm(maxMemory int64) error {

...

f, err := mr.ReadForm(maxMemory)

...

}

func (r *Reader) ReadForm(maxMemory int64) (*Form, error) {

return r.readForm(maxMemory)

}

func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {

...

n, err := io.CopyN(&b, p, maxMemory+1)

...

if n > maxMemory {

可以看到这里把剩余的内容写入到临时文件了

file, err := ioutil.TempFile("", "multipart-")

size, err := io.Copy(file, io.MultiReader(&b, p))

...

}

...

}

解决方案一

直接限制客户端上传大小,但是有时候这个数字不通用而且不合适,而且文件类型的检查也没法做

r.Body = http.MaxBytesReader(w, r.Body, 32<<20+512)

解决方案二

自己逐步解析

r.Body = http.MaxBytesReader(w, r.Body, 32<<20+1024)

reader, err := r.MultipartReader()

if err != nil {

http.Error(w, err.Error(), http.StatusBadRequest)

return

}

// parse text field

text := make([]byte, 512)

p, err := reader.NextPart()

// one more field to parse, EOF is considered as failure here

if err != nil {

http.Error(w, err.Error(), http.StatusInternalServerError)

return

}

if p.FormName() != "text_field" {

http.Error(w, "text_field is expected", http.StatusBadRequest)

return

}

_, err = p.Read(text)

if err != nil && err != io.EOF {

http.Error(w, err.Error(), http.StatusInternalServerError)

return

}

// parse file field

p, err = reader.NextPart()

if err != nil && err != io.EOF {

http.Error(w, err.Error(), http.StatusInternalServerError)

return

}

if p.FormName() != "file_field" {

http.Error(w, "file_field is expected", http.StatusBadRequest)

return

}

buf := bufio.NewReader(p)

sniff, _ := buf.Peek(512)

contentType := http.DetectContentType(sniff)

if contentType != "application/zip" {

http.Error(w, "file type not allowed", http.StatusBadRequest)

return

}

f, err := ioutil.TempFile("", "")

if err != nil {

http.Error(w, err.Error(), http.StatusInternalServerError)

return

}

defer f.Close()

var maxSize int64 = 32 << 20

lmt := io.MultiReader(buf, io.LimitReader(p, maxSize - 511))

written, err := io.Copy(f, lmt)

if err != nil && err != io.EOF {

http.Error(w, err.Error(), http.StatusInternalServerError)

return

}

if written > maxSize {

os.Remove(f.Name())

http.Error(w, "file size over limit", http.StatusBadRequest)

return

}

提示:

只在POST主体中获取前两个部分,处理程序将期望text_field然后file_field

使用bufio.Reader包装part reader(先查看512字节)断言文件类型

使用io.LimitReader限制文件大小(使用1个字节的偏移量来查看part reader是否还有一些数据)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值