为了账号安全,请及时绑定邮箱和手机立即绑定

如何上传 gzip 压缩文件而不将所有内容读入内存

如何上传 gzip 压缩文件而不将所有内容读入内存

Go
慕森卡 2023-08-07 19:01:15
我想知道是否有人可以指出如何进一步优化?我不喜欢这样的事实:我必须将整个文件读入内存并创建文件长度的字节切片。这是代码:func newfileUploadRequestWithGzip(uri string, paramName, path string) (*http.Request, error) {    f, err := os.Open(path)    if err != nil {        return nil, err    }    fi, err := f.Stat()    if err != nil {        return nil, err    }    defer f.Close()    body := new(bytes.Buffer)    writer := multipart.NewWriter(body)    part, err := writer.CreateFormFile(paramName, fi.Name())    if err != nil {        return nil, err    }    filebuffer := make([]byte, fi.Size())    var gzbuffer bytes.Buffer    gw, err := gzip.NewWriterLevel(&gzbuffer, gzip.DefaultCompression)    buffer := bufio.NewReader(f)    if _, err = buffer.Read(filebuffer); err != nil {        fmt.Printf("Error in reading file with error: %v\n", err)    }    n, err := gw.Write(filebuffer)    gw.Close()    fmt.Printf("%d:%d => %.2f%%\n", n, len(gzbuffer.Bytes()), float32(len(gzbuffer.Bytes()))/float32(n)*100.0)    io.Copy(part, &gzbuffer)    if writer.Close() != nil {        return nil, err    }    request, requestErr := http.NewRequest("POST", uri, body)    request.Header.Add("Content-Type", writer.FormDataContentType())    return request, requestErr}
查看完整描述

1 回答

?
慕妹3242003

TA贡献1824条经验 获得超6个赞

我认为没有办法在不将文件读入内存的情况下发送文件;但是,无需立即将其所有内容读入内存即可完成。


如果您不想立即将所有内容读入内存,那就不要这样做 - 并摆脱bytes.Buffer. 和gzip.NewWriter都multipart.Writer.CreateFormFile可以很好地与io.Writer界面配合使用,而不仅仅是bytes.Buffer. http.NewRequest,另一方面,需要一个io.Reader. 虽然bytes.Buffer这似乎是实现这两者的最佳选择,但它并不是唯一的选择。io包提供了一个io.Pipe()创建高效的内存管道,这正是我们这里所需要的。


例如,


func newfileUploadRequestWithGzip(uri string, paramName, path string) (*http.Request, error) {

    f, err := os.Open(path)

    if err != nil {

        return nil, err

    }


    fi, err := f.Stat()

    if err != nil {

        f.Close()

        return nil, err

    }


    buf := bufio.NewReader(f)

    r, w := io.Pipe()


    multi := multipart.NewWriter(w)

    part, err := multi.CreateFormFile(paramName, fi.Name())

    if err != nil {

        f.Close()

        return nil, err

    }

    writer, err := gzip.NewWriterLevel(part, gzip.DefaultCompression)

    if err != nil {

        f.Close()

        return nil, err

    }


    go func() {

        _,err := io.Copy(writer, buf)

        w.Close()

        multi.Close()

        writer.Close()

        f.Close()

        if err != nil {

            panic(err) // panic is not good, but how to make it good depends.

        }

    }()


    request, requestErr := http.NewRequest("POST", uri, r)

    request.Header.Add("Content-Type", multi.FormDataContentType())

    return request, requestErr

}

这段代码非常臭,需要一些工作来重构。您的函数签名以及函数的范围(其职责)或您希望函数执行的操作需要更改,以实现更好的错误处理和资源管理。建议拆分功能 - 一个用于准备io.Writers和io.Pipe(),一个用于多部分处理,一个用于请求部分;将整个事物包装成一个类型并创建私有辅助方法,同时设置错误并使用一种Err() error方法来收集错误。但如果不了解用例,就很难做出决定并使其付诸实践——而且主要是基于意见。


@mh-cbon 这里提供了一个更好看的代码:https ://play.golang.org/p/i5bocA6-Q4M


查看完整回答
反对 回复 2023-08-07
  • 1 回答
  • 0 关注
  • 127 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信