2017-10-17 35 views
5

Tôi muốn phục vụ tệp .ZIP được tạo khi đang di chuyển mà không phải ghi nó vào đĩa (I/O sẽ làm chậm hiệu năng) và phân phối nó cho khách hàng qua HTTP.Tạo + Phân phối (qua HTTP) tệp .ZIP, mà không cần ghi vào đĩa?

Sau đây là cách tôi lần đầu tiên cố gắng này:

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

} 

Đây không phải là tốt, như ZIP kết thúc lên được tham nhũng sau khi tải về. Tôi cho rằng vấn đề có liên quan đến io.Copy; tôi sẽ sử dụng một phương pháp khác?

+1

Bạn đang sao chép trình ghi zip trước khi đóng. – JimB

+0

@JimB cái nào được ưu tiên ?, cho 'io.Copy (w, buf)' hoặc thành 'w.Write (buf.Bytes())' – nbari

+1

@nbari: Vì chúng ta biết rằng 'buf' là một' byte.Buffer Vì vậy, toàn bộ tập tin đã có trong một lát, tôi muốn thứ hai kể từ khi chúng tôi về mặt kỹ thuật không cần các cuộc gọi Copy. Trong thực tế, hoặc là tốt. – JimB

Trả lời

2

tôi thấy thú vị này và chỉ để thử nghiệm đến với điều này:

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

Tôi chỉ cần thêm một số tiêu đề như Content-TypeContent-Disposition.

Cũng thay vì sử dụng io.Copy(w, buf) Tôi đang viết trực tiếp w.Write(buf.Bytes()) tự hỏi điều này có tốt hơn không? có lẽ một người dùng có kinh nghiệm hơn có thể làm rõ điều này.

1

Đây là phương pháp đơn giản hơn một chút khi sử dụng io.Copy. Nó có thể không hoạt động như sử dụng bộ đệm cho kích thước tệp lớn hơn, nhưng nó hoạt động cho tôi:

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) 
    } 
} 
Các vấn đề liên quan