如何使用 Gin 在 HTTP 服务器中动态生成 zip/7z 存档?

Posted

技术标签:

【中文标题】如何使用 Gin 在 HTTP 服务器中动态生成 zip/7z 存档?【英文标题】:How to generate zip / 7z archive on the fly in a HTTP server using Gin? 【发布时间】:2019-08-09 11:37:23 【问题描述】:

我使用Gin创建一个HTTP服务器,我想给用户一个动态生成的压缩包。

理论上,我可以先在文件系统上生成一个 zip 文件,然后再提供它。但这确实是一个糟糕的方法(在开始下载前等待 5 分钟)。我想立即开始将其提供给用户并在生成内容时推送内容。

我找到了 DataFromReader (example),但在存档完成之前无法知道 ContentLength。

func DownloadEndpoint(c *gin.Context) 
    ...
    c.DataFromReader(
        http.StatusOK,
        ContentLength,
        ContentType,
        Body,
        map[string]string
            "Content-Disposition": "attachment; filename=\"archive.zip\""),
        ,
    )

我该怎么做?

【问题讨论】:

直接使用Stream method或ResponseWriter。 【参考方案1】:

使用stream method 和archive/zip,您可以动态创建zip 并将它们流式传输到服务器。

package main

import (
    "os"

    "archive/zip"

    "github.com/gin-gonic/gin"
)

func main() 

    r := gin.Default()
    r.GET("/", func(c *gin.Context) 

        c.Writer.Header().Set("Content-type", "application/octet-stream")
        c.Stream(func(w io.Writer) bool 

            // Create a zip archive.
            ar := zip.NewWriter(w)

            file1, _ := os.Open("filename1")
            file2, _ := os.Open("filename2")
            c.Writer.Header().Set("Content-Disposition", "attachment; filename='filename.zip'")

            f1, _ := ar.Create("filename1")
            io.Copy(f1, file1)
            f2, _ := ar.Create("filename2")
            io.Copy(f2, file2)

            ar.Close()

            return false
        )
    )
    r.Run()

直接使用ResponseWriter

package main

import (
    "io"
    "os"

    "archive/zip"

    "github.com/gin-gonic/gin"
)


func main() 

    r := gin.Default()
    r.GET("/", func(c *gin.Context) 
        c.Writer.Header().Set("Content-type", "application/octet-stream")
        c.Writer.Header().Set("Content-Disposition", "attachment; filename='filename.zip'")
        ar :=  zip.NewWriter(c.Writer)
        file1, _ := os.Open("filename1")
        file2, _ := os.Open("filename2")
        f1, _ := ar.Create("filename1")
        io.Copy(f1, file1)
        f2, _ := ar.Create("filename1")
        io.Copy(f1, file2)
        ar.Close()
    )
    r.Run()

【讨论】:

既然可以将 zip 写入 w,为什么还要将其写入缓冲区? 正如@MarkusWMahlberg 所说,您应该将zip.NewWriter 调用移至函数闭包内,并将其直接转到w,而无需创建或使用bytes.Buffer 或调用最终的io.Copy .他们以这种方式拥有它,因为在将任何内容写入 HTTP 连接之前,压缩已在内存中完全完成,因此流式传输毫无意义。 使用流闭包提供的io.writer 来创建zip存档是有意义的 对我来说很好用,谢谢!顺便说一句,代码有一个小错误,我认为我们应该使用filename=\"filename.zip\"而不是filename='filename.zip'

以上是关于如何使用 Gin 在 HTTP 服务器中动态生成 zip/7z 存档?的主要内容,如果未能解决你的问题,请参考以下文章

(gin框架拓展)两种 HTTP 请求方法:GET 和 POST

无法生成gin-gonic服务器应用程序的代码覆盖率报告

Gin 如何编写一个接收文件的 HTTP 接口

Gin 如何编写一个接收文件的 HTTP 接口

Gin 优雅设置回包

gin-session使用以及源码分析