2017-10-17 2 views
5

Ich möchte eine .ZIP-Datei bereitstellen, die im laufenden Betrieb erstellt wird, ohne auf die Festplatte schreiben zu müssen (I/O wird die Leistung verlangsamen) und über HTTP an den Client zu senden.Create + Serve (über HTTP) eine .ZIP-Datei, ohne auf die Festplatte zu schreiben?

Hier ist, wie ich zum ersten Mal das versucht:

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) 
} 

} 

Das ist nicht gut, da die .ZIP korrupt endet als nach dem Download. Ich nehme an, das Problem hat mit io.Copy zu tun; soll ich eine andere Methode verwenden?

+1

Sie kopieren den Zip-Writer, bevor Sie es schließen. – JimB

+0

@ JimB was ist bevorzugt ?, um 'io.Copy (w, buf)' oder 'w.Write (buf.Bytes())' – nbari

+1

@ nbari: Da wir wissen, dass 'buf' ist ein' Bytes.Buffer 'Also ist die gesamte Datei bereits in einer Scheibe, ich würde die zweite bevorzugen, da wir technisch den Kopieraufruf nicht brauchen. In der Praxis ist beides in Ordnung. – JimB

Antwort

2

Das fand ich interessant und nur zum Testen kam mit dieser:

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) 
} 

Ich füge nur einige Header wie die Content-Type und Content-Disposition.

Auch statt io.Copy(w, buf) schreibe ich direkt w.Write(buf.Bytes()) frage mich, ob das besser ist? wahrscheinlich könnte ein erfahrenerer Benutzer dies klären.

1

Hier ist eine etwas einfachere Methode mit io.Copy. Es ist wahrscheinlich nicht so leistungsfähig wie die Verwendung eines Puffers für größere Dateigrößen, aber es funktioniert für mich:

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) 
    } 
} 
Verwandte Themen