IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    关于一个打包下载的需求

    老王发表于 2022-07-01 06:01:41
    love 0

    前些天遇到一个「打包下载」的需求,在调研过程中走了一些弯路,本文记录一下。

    比如说某网站有一个文件列表,用户点哪个就可以下载哪个,如果用户想下载多个,无非就是多点几次而已。于是需求来了:当用户想下载多个文件的时候,可以通过一次点击完成打包下载操作。

    听起来似乎并不复杂,服务端可以把用户想要下载的文件打包成一个新文件,然后用户点一次就可以下载了,但是这样做有以下几个缺点:

    • 浪费了时间,多了创建新文件的流程。
    • 浪费了空间,同样的文件被多次存储。
    • 用户体验差,下载必须要等到新文件创建好才能开始。

    不难得出结论:动态流式下载才是正解,同事提到 tar 可以搞定,于是研究一下:

    shell> cat test_0.txt 
    xxx
    xxx
    shell> cat test_1.txt 
    yyy
    yyy
    shell> tar cf test.tar test_0.txt test_1.txt
    shell> cat test.tar 
    test_0.txt00006440...01014257504126011510 0ustar rootrootxxx
    xxx
    test_1.txt00006440...01014257504241011507 0ustar rootrootyyy
    yyy

    如上可见,tar 文件的格式非常简单,多个文件的内容从上到下依次排列,只不过每个文件内容的前面附加了一个头,其中保存了诸如文件名,权限之类的信息。

    看上去用 tar 的话确实可以搞定动态流式下载,不过 tar 有个缺点,普通用户搞不清 tar 文件类型是什么东西,相比较而言,他们更乐于接受 zip 文件类型。

    不过 zip 文件类型的格式可要比 tar 复杂,我从 wikipedia 找到下图:

    zip

    zip

    对于凡夫俗子的我来说,想要通过手撸 zip 格式来实现动态流式下载绝非易事,就在举棋不定之际,我突然发现 golang 的 zip 标准库已经实现了 Writer 接口,这就意味着,我们只要结合使用 zip.NewWriter 和 http.ResponseWriter 就能实现我们的目的:

    package main
    
    import (
    	"archive/zip"
    	"fmt"
    	"io"
    	"net/http"
    	"os"
    )
    
    func main() {
    	http.HandleFunc("/test", test)
    	http.ListenAndServe(":8080", nil)
    }
    
    func test(w http.ResponseWriter, r *http.Request) {
    	w.Header().Set("Content-Type", "application/zip")
    	w.Header().Set("Content-Disposition", "attachment; filename=test.zip")
    	writer := zip.NewWriter(w)
    	for i := 0; i < 2; i++ {
    		name := fmt.Sprintf("test_%d.txt", i)
    		srcFile, err := os.Open(name)
    		if err != nil {
    			panic(err)
    		}
    		defer srcFile.Close()
    		dstFile, err := writer.Create(name)
    		if err != nil {
    			panic(err)
    		}
    		if _, err := io.Copy(dstFile, srcFile); err != nil {
    			panic(err)
    		}
    	}
    	writer.Close()
    }
    

    如上代码编译运行后,打开浏览器,执行 http://localhost:8080/test 即可看到效果。



沪ICP备19023445号-2号
友情链接