Gin: file close?

Created on 24 Oct 2019  ·  8Comments  ·  Source: gin-gonic/gin

All 8 comments

能描述的再清楚些吗? 提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泄露?我以前测试过,没有复现出来。有可复现的服务端和客户端代码吗?

可以复现。按代码逻辑,你上传个比较大的文件,就会出现。具体多大得看你配置信息。

2114 merged! thanks!

Was this page helpful?
0 / 5 - 0 ratings