使用中间资源时如何处理函数中的“defer”

use*_*261 1 go

给定以下函数

func jsonBodyReader(config Config, r *http.Request) (io.Reader, error) {
    bodyReader := io.LimitReader(r.Body, config.Reports.Max.Body)
    // defer r.Body.Close()

    gzipReader, err := gzip.NewReader(bodyReader)
    if err != nil {
        return nil, err
    }
    // defer gzipReader.Close()

    return io.LimitReader(gzipReader, config.Reports.Max.Json), nil
}
Run Code Online (Sandbox Code Playgroud)

r.Body处理和延迟关闭的最佳方法是什么gzipReader

是否真的有必要始终返回所有中间资源/流/读取器才能在父函数中推迟/关闭它们?

另一个例子

func save(target string, reader io.Reader) (io.Reader, *os.File, error) {
    file, err := os.Create(target)
    if err != nil {
        return reader, nil, err
    }
    /// defer file.Close()

    return io.TeeReader(reader, file), file, nil
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我必须返回os.File才能打电话file.Close()给家长。

我发现了这个相关的问题:当资源超出周围函数的范围时,如何推迟资源清理?不幸的是,建议的解决方案对于我的用例来说仍然很尴尬。

Bur*_*dar 6

如果函数的调用者必须从函数打开或修改的文件或流中读取数据,则不能在该函数中延迟关闭它。当函数返回时,流或文件将被关闭,从而使尝试从中读取数据的调用者失败。

在第一个示例中,您有两个受限的读者,一个用于限制正文,另一个用于限制解压缩的 json。你可以这样做:

func jsonBodyReader(config Config, r *http.Request) (io.ReadCloser, error) {
    bodyReader := io.LimitReader(r.Body, config.Reports.Max.Body)
    gzipReader, err := gzip.NewReader(bodyReader)
    if err != nil {
        return nil, err
    }
    return readerCloser{
       Reader:io.LimitReader(gzipReader, config.Reports.Max.Json),
       cl: r.Body,
    }, nil
}
Run Code Online (Sandbox Code Playgroud)

在哪里

type readerCloser struct {
   io.Reader
   cl io.Closer
}

func (r *readerCloser) Close() error { return r.cl.Close() }
Run Code Online (Sandbox Code Playgroud)

readerCloser类型允许您返回ReaderCloser调用者可以读取并关闭的 a ,这将关闭源流。