I use Gin to create a HTTP server and I want to give a dynamically generated zip archive to the user.
Theoretically I could first generate a zip file on a file system and then serve it. But that is really a bad way (to wait 5 mins before starting download). I want start giving it to a user immediately and push content as it is generated.
I've found DataFromReader (example) but ContentLength is not known until archive is done.
func DownloadEndpoint(c *gin.Context) {
...
c.DataFromReader(
http.StatusOK,
ContentLength,
ContentType,
Body,
map[string]string{
"Content-Disposition": "attachment; filename=\"archive.zip\""),
},
)
}
How can I do that?
Using the stream method and archive/zip you can create zip on the fly and stream them to the server.
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()
}
By using directly 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()
}