猿问

如何在不将所有内容读入内存的情况下上传 gzip 文件

我想知道是否有人可以指出如何进一步优化?我不喜欢我必须将整个文件读入内存并创建文件长度的字节片的事实。


这是代码:


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

}



郎朗坤
浏览 175回答 1
1回答

慕森王

我认为没有办法在不将文件读入内存的情况下发送文件;但是,它可以在不一次将其所有内容读入内存的情况下完成。如果您不想一次将所有内容读入内存,请不要这样做 - 并摆脱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方法来收集错误。但在不了解用例的情况下,很难做出决定并使其变得实用——而且大多基于意见。
随时随地看视频慕课网APP

相关分类

Go
我要回答