2017-10-17 17 views
5

Chcę obsługiwać plik .ZIP, który jest tworzony w locie, bez konieczności zapisywania go na dysku (operacje we/wy spowolnią wydajność) i dostarczania go do klienta za pośrednictwem protokołu HTTP.Twórz + udostępniaj (przez HTTP) plik .ZIP, bez zapisywania na dysku?

Oto jak ja to pierwsza próba:

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

} 

To nie jest dobre, jak kończy się .zip jest uszkodzony po pobraniu. Przypuszczam, że problem związany jest z io.Copy; czy będę używał innej metody?

+1

Kopiujesz program zapisujący zip, zanim go zamkniesz. – JimB

+0

@JimB co jest preferowane ?, do 'io.Copy (w, buf)' lub do 'w.Write (buf.Bytes())' – nbari

+1

@nbari: Ponieważ wiemy, że 'buf' to' bytes.Buffer "więc cały plik jest już w plasterku, wolałbym drugi, ponieważ technicznie nie potrzebujemy wywołania Copy. W praktyce albo jest w porządku. – JimB

Odpowiedz

2

znalazłem ciekawy i to tylko do testowania wymyślił to:

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

po prostu dodać kilka nagłówków jak Content-Type i Content-Disposition.

Również zamiast pisać, piszę bezpośrednio w.Write(buf.Bytes()) zastanawiając się, czy to jest lepsze? prawdopodobnie bardziej doświadczony użytkownik mógłby to wyjaśnić.

1

Oto nieco prostsza metoda z użyciem io.Copy. Prawdopodobnie nie jest tak wydajny jak użycie buforów dla większych rozmiarów plików, ale działa to dla mnie:

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) 
    } 
} 
Powiązane problemy