首页IT科技gin框架结构(gin框架中如何实现流式下载)

gin框架结构(gin框架中如何实现流式下载)

时间2025-07-31 03:52:58分类IT科技浏览5794
导读:作者:张富春(ahfuzhang ,转载时请注明作者和引用链接,谢谢!...

作者:张富春(ahfuzhang)              ,转载时请注明作者和引用链接                      ,谢谢!

cnblogs博客 zhihu Github 公众号:一本正经的瞎扯

团队中之前的文件下载做得比较复杂       ,因为担心量太大       ,是后台做异步的下载                      ,最终生成文件               ,传送文件到CDN服务器       ,最后再告诉用户下载链接              。

其实在查询接口中就可以实现流式下载                     ,这样查询接口和下载接口可以合二为一               ,更加简单                      。

下面是我的demo:

1.建立一个download_file的文件夹作为项目文件夹

go mod init download_file

2.生成go.mod文件,并准备对应的包:

go get github.com/gin-gonic/gin@latest go get github.com/gin-contrib/gzip

go.mod文件内容如下:

module download_file go 1.17 require github.com/gin-gonic/gin v1.8.1 require ( github.com/gin-contrib/gzip v0.0.6 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/validator/v10 v10.10.0 // indirect github.com/goccy/go-json v0.9.7 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.1 // indirect github.com/ugorji/go/codec v1.2.7 // indirect golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect golang.org/x/text v0.3.6 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect )

3.main.go文件:

3.1 初始化gin框架

func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) engine := gin.New() // engine.Use(gzip.Gzip(gzip.DefaultCompression)) //如果需要开启gzip压缩                     ,取消这一行的注释 engine.Handle("POST", "/query", downloadFile) // 假定查询和下载接口都是这条接口实现 engine.Handle("GET", "/", homepage) engine.Run(":8080") }

3.2 下载链接页                      ,模拟post到新窗口的场景

func homepage(ctx *gin.Context) { ctx.Header("Content-Type", "text/html") ctx.Writer.WriteString(` <html> <body> open window and to download: <a href="javascript:download()">download</a> <script> function download(){ var handle = window.open("about:blank", "my_download_window"); document.forms[0].target = "my_download_window"; document.forms[0].json.value="ahfu test"; document.forms[0].submit(); } </script> <form action="/query" method="POST" enctype="multipart/form-data"> <input type="hidden" name="json" value=""/> </form> </body> </html> `) }

点击链接后,弹出新窗口              ,在新窗口中POST json数据

3.3 流式下载功能

func downloadFile(ctx *gin.Context) { reqData, has := ctx.GetPostForm("json") if !has { ctx.Data(400, "text/plain","not found json form data") return } // 此处省略查询的业务逻辑 // todo: // 下面开始下载的准备 ctx.Writer.WriteHeader(200) ctx.Header("Content-Type", "text/plain; charset=utf-8") ctx.Header("Transfer-Encoding", "chunked") // 告诉浏览器                      ,分段的流式的输出数据 // ctx.Header("Content-Encoding", "gzip") // 输出不是gzip内容       ,又加上这个头              ,浏览器会拒收       。这里是个实验                      ,不要加这行代码 now := time.Now() fileName := now.Format("20060102_150405.csv") ctx.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", fileName)) // 设置下载的文件名 ctx.Writer.WriteHeaderNow() // 下面模拟一个周期非常长的数据处理和下载过程 for i := 0; i < 100; i++ { ctx.Writer.WriteString("\"") ctx.Writer.WriteString(str) ctx.Writer.WriteString("\"\t") ctx.Writer.WriteString("\"") ctx.Writer.WriteString(time.Now().Format("2006-01-02 15:04:05")) ctx.Writer.WriteString("\"\n") ctx.Writer.Flush() // 产生一定的数据后       , flush到浏览器端 time.Sleep(time.Duration(500) * time.Millisecond) } }

打开浏览器       ,输入:http://127.0.0.1:8080

然后点击链接                      ,过一会儿后会出现文件下载框       。点击保存后               ,可以看见陆续下载文件的过程                      。

注意:为什么过了一会儿才出现文件下载框?这是由于浏览器的缓冲机制导致的               。如果一开始下载的字节数很多       ,就会很快出现下载框

3.4 启用gzip压缩

大流量的文本下载                     ,可能很占带宽               ,我们可以开启GZIP压缩:

func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) engine := gin.New() engine.Use(gzip.Gzip(gzip.DefaultCompression)) //如果需要开启gzip压缩,取消这一行的注释 engine.Handle("POST", "/query", downloadFile) // 假定查询和下载接口都是这条接口实现 engine.Handle("GET", "/", homepage) engine.Run(":8080") }

gin框架中已经提供gzip压缩的能力       。

3.5 完整代码:

// main.go package main import ( "fmt" "log" "time" "github.com/gin-contrib/gzip" "github.com/gin-gonic/gin" ) func useGzip(engine *gin.Engine) { engine.Use(gzip.Gzip(gzip.DefaultCompression)) } func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) engine := gin.New() // engine.Use(gzip.Gzip(gzip.DefaultCompression)) //如果需要开启gzip压缩                     ,取消这一行的注释 engine.Handle("POST", "/query", downloadFile) engine.Handle("GET", "/", homepage) engine.Run(":8080") } func downloadFile(ctx *gin.Context) { reqData, has := ctx.GetPostForm("json") if !has { ctx.Data(400, "text/plain","not found json form data") return } // 此处省略查询的业务逻辑 // todo: // 下面开始下载的准备 ctx.Writer.WriteHeader(200) ctx.Header("Content-Type", "text/plain; charset=utf-8") ctx.Header("Transfer-Encoding", "chunked") // 告诉浏览器                      ,分段的流式的输出数据 // ctx.Header("Content-Encoding", "gzip") // 输出不是gzip内容,又加上这个头              ,浏览器会拒收                     。这里是个实验                      ,不要加这行代码 now := time.Now() fileName := now.Format("20060102_150405.csv") ctx.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", fileName)) // 设置下载的文件名 ctx.Writer.WriteHeaderNow() // 下面模拟一个周期非常长的数据处理和下载过程 for i := 0; i < 100; i++ { ctx.Writer.WriteString("\"") ctx.Writer.WriteString(str) ctx.Writer.WriteString("\"\t") ctx.Writer.WriteString("\"") ctx.Writer.WriteString(time.Now().Format("2006-01-02 15:04:05")) ctx.Writer.WriteString("\"\n") ctx.Writer.Flush() // 产生一定的数据后       , flush到浏览器端 time.Sleep(time.Duration(500) * time.Millisecond) } } func homepage(ctx *gin.Context) { ctx.Header("Content-Type", "text/html") ctx.Writer.WriteString(` <html> <body> open window and to download: <a href="javascript:download()">download</a> <script> function download(){ var handle = window.open("about:blank", "my_download_window"); document.forms[0].target = "my_download_window"; document.forms[0].json.value="ahfu test"; document.forms[0].submit(); } </script> <form action="/query" method="POST" enctype="multipart/form-data"> <input type="hidden" name="json" value=""/> </form> </body> </html> `) }

have fun. ?

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
王者孙尚香出装技巧装备详解大全(王者荣耀中孙尚香怎么出装?) ai制作数字油画图片(AI数字绘画 stable-diffusion 保姆级教程)