能描述的再清楚些吗? 提Issue的方式方法建议先去看一下.
内部调用的时候,是调用
func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error)
multipart.File 有可能是已经打开的文件句柄。这里直接忽略了,需要Close一下。
我大致看了一下multipart包, 作为http.Request.FormFile方法返回的第一个参数, 其类型File为一个接口, 主要作用是访问存储在内存或者磁盘上的文件, 而第二个参数为*FileHeader类型.
之所以丢弃返回的第一个的原因是, file正是调用fileHeader.Open()产生的结果, 因此没有必要返回第一个参数, 我们从net/http.Request.FormFile()的源码中可以看出:
// FormFile returns the first file for the provided form key.
// FormFile calls ParseMultipartForm and ParseForm if necessary.
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
}
如若返回的file不是nil, 那么一定是调用第一个文件头的Open()方法的结果. gin只是重新封装了一下FormFIle方法并且做了一定的取舍, 但是不妨碍使用.
关于您提出的情况, 关于文件close()或者GC, 应该在具体的业务逻辑中由开发者自己完成.
// Open opens and returns the FileHeader's associated File.
func (fh *FileHeader) Open() (File, error) {
if b := fh.content; b != nil {
r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b)))
return sectionReadCloser{r}, nil
}
return os.Open(fh.tmpfile)
}
是可以没有必要返回第一个参数。但是第一个参数有可能是os.File。
有打开文件的句柄就需要去关闭。直接忽略了,可能会导致句柄泄露。
我测试了一下, 您说的是正确的, 即便直接丢弃也会导致文件句柄泄露的情况, gin这一点确实有问题.
可以复现fd泄露?我以前测试过,没有复现出来。有可复现的服务端和客户端代码吗?
可以复现。按代码逻辑,你上传个比较大的文件,就会出现。具体多大得看你配置信息。