Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

file close? #2104

Closed
linfangrong opened this issue Oct 24, 2019 · 8 comments
Closed

file close? #2104

linfangrong opened this issue Oct 24, 2019 · 8 comments

Comments

@linfangrong
Copy link
Contributor

gin/context.go

Line 519 in 0ce4661

_, fh, err := c.Request.FormFile(name)

@ShiinaOrez
Copy link

能描述的再清楚些吗? 提Issue的方式方法建议先去看一下.

@linfangrong
Copy link
Contributor Author

内部调用的时候,是调用
func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error)

multipart.File 有可能是已经打开的文件句柄。这里直接忽略了,需要Close一下。

@ShiinaOrez
Copy link

我大致看了一下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, 应该在具体的业务逻辑中由开发者自己完成.

@linfangrong
Copy link
Contributor Author

// 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。
有打开文件的句柄就需要去关闭。直接忽略了,可能会导致句柄泄露。

@ShiinaOrez
Copy link

我测试了一下, 您说的是正确的, 即便直接丢弃也会导致文件句柄泄露的情况, gin这一点确实有问题.

@guonaihong
Copy link
Contributor

guonaihong commented Oct 30, 2019

可以复现fd泄露?我以前测试过,没有复现出来。有可复现的服务端和客户端代码吗?

@linfangrong
Copy link
Contributor Author

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

@thinkerou
Copy link
Member

#2114 merged! thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants