创建+服务(通过HTTP).ZIP文件,而不写入磁盘?
问题描述:
我想提供一个即时创建的.ZIP文件,而不必将其写入磁盘(I/O会降低性能)并通过HTTP将其提供给客户端。创建+服务(通过HTTP).ZIP文件,而不写入磁盘?
这是我第一次尝试这样的:
func ZipServe(W http.ResponseWriter, R *http.Request) {
buf := new(bytes.Buffer)
writer := zip.NewWriter(buf)
// for the sake of this demonstration, this is the data I will zip
data := ioutil.ReadFile("randomfile.jpg")
f, err := writer.Create("randomfile.jpg")
if err != nil {
fmt.Println(err)
}
_, err = f.Write(data)
if err != nil {
fmt.Println(err)
}
io.Copy(W, buf)
err := writer.Close()
if err != nil {
fmt.Println(err)
}
}
这是不行的,因为.ZIP最终被下载后损坏。我想这个问题与io.Copy有关;我应该使用不同的方法吗?
答
我发现有趣这一点,只是为了测试想出了这个:
http://play.golang.org/p/JKAde2jbR3
package main
import (
"archive/zip"
"bytes"
"fmt"
"io/ioutil"
"log"
"net/http"
)
func zipHandler(w http.ResponseWriter, r *http.Request) {
filename := "randomfile.jpg"
buf := new(bytes.Buffer)
writer := zip.NewWriter(buf)
data, err := ioutil.ReadFile(filename)
if err != nil {
log.Fatal(err)
}
f, err := writer.Create(filename)
if err != nil {
log.Fatal(err)
}
_, err = f.Write([]byte(data))
if err != nil {
log.Fatal(err)
}
err = writer.Close()
if err != nil {
log.Fatal(err)
}
w.Header().Set("Content-Type", "application/zip")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.zip\"", filename))
//io.Copy(w, buf)
w.Write(buf.Bytes())
}
func main() {
http.HandleFunc("/zip", zipHandler)
http.ListenAndServe(":8080", nil)
}
我只是添加一些标题,如Content-Type
和Content-Disposition
。
而不是使用io.Copy(w, buf)
我直接写w.Write(buf.Bytes())
想知道这是否更好?可能更有经验的用户可以澄清这一点。
答
这是一个使用io.Copy稍微简单一些的方法。这可能不像使用缓冲区来保存更大的文件大小,但它适用于我:
func handleZip(w http.ResponseWriter, r *http.Request) {
f, err := os.Open("main.go")
if err != nil {
log.Fatal(err)
}
defer func() {
if err := f.Close(); err != nil {
log.Fatal(err)
}
}()
// write straight to the http.ResponseWriter
zw := zip.NewWriter(w)
cf, err := zw.Create(f.Name())
if err != nil {
log.Fatal(err)
}
w.Header().Set("Content-Type", "application/zip")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.zip\"", f.Name()))
// copy the file contents to the zip Writer
_, err = io.Copy(cf, f)
if err != nil {
log.Fatal(err)
}
// close the zip Writer to flush the contents to the ResponseWriter
err = zw.Close()
if err != nil {
log.Fatal(err)
}
}
您正在复制zip作家,然后关闭它。 – JimB
@JimB什么是首选?,以'io.Copy(w,buf)'或'w.Write(buf.Bytes())' – nbari
@nbari:因为我们知道'buf'是'bytes.Buffer '所以整个文件已经在一个切片中,我更喜欢第二个,因为我们在技术上不需要Copy调用。在实践中或者没有问题。 – JimB