gzip压缩到http responseWriter

问题描述:

我是新来的Go。但我正在玩REST Api。我不能得到相同的行为进行json.Marshal作为json.Encoder的两个功能我有gzip压缩到http responseWriter

我想利用这个功能给gzip我的回应:

func gzipFast(a *[]byte) []byte { 
    var b bytes.Buffer 
    gz := gzip.NewWriter(&b) 
    defer gz.Close() 
    if _, err := gz.Write(*a); err != nil { 
     panic(err) 
    } 
    return b.Bytes() 
} 

但这个函数返回此:

curl http://localhost:8081/compressedget --compressed --verbose 
* Trying 127.0.0.1... 
* Connected to localhost (127.0.0.1) port 8081 (#0) 
> GET /compressedget HTTP/1.1 
> Host: localhost:8081 
> User-Agent: curl/7.47.0 
> Accept: */* 
> Accept-Encoding: deflate, gzip 
> 
< HTTP/1.1 200 OK 
< Content-Encoding: gzip 
< Content-Type: application/json 
< Date: Wed, 24 Aug 2016 00:59:38 GMT 
< Content-Length: 30 
< 
* Connection #0 to host localhost left intact 

这里是去功能:

func CompressedGet(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { 

    box := Box{Width: 10, Height: 20, Color: "gree", Open: false} 
    box.ars = make([]int, 100) 
    for i := range box.ars { 
     box.ars[i] = i 
    } 
    //fmt.Println(r.Header.Get("Content-Encoding")) 

    w.Header().Set("Content-Type", "application/json") 
    w.Header().Set("Content-Encoding", "gzip") 
    b, _ := json.Marshal(box) 
    //fmt.Println(len(b)) 
    //fmt.Println(len(gzipFast(&b))) 

    fmt.Fprint(w, gzipFast(&b)) 
    //fmt.Println(len(gzipSlow(b))) 
    //gz := gzip.NewWriter(w) 
    //defer gz.Close() 
    //json.NewEncoder(gz).Encode(box) 
    r.Body.Close() 

} 

但是,当我取消:

//gz := gzip.NewWriter(w) 
//defer gz.Close() 
//json.NewEncoder(gz).Encode(box) 

它工作正常。

+0

也许因为3条线是不必要的? gzipFast函数已经在对json编码框进行gzip压缩。 – Sridhar

您需要在访问基础字节之前刷新或关闭gzip写入器,例如,

func gzipFast(a *[]byte) []byte { 
    var b bytes.Buffer 
    gz := gzip.NewWriter(&b) 
    if _, err := gz.Write(*a); err != nil { 
     gz.Close() 
     panic(err) 
    } 
    gz.Close() 
    return b.Bytes() 
} 

否则什么是gzip写入器中的缓冲区,但尚未写出到最后的流没有被收集起来。

+0

这解决了它。谢谢! – stihl

我觉得问题是使用fmt.Fprint(w, gzipFast(&b))

如果你看看gzipFast的定义,它会返回一个[]byte。您正在将此切片放入打印功能中,该功能将所有内容“打印”为w

如果你看一下io.Writer的定义:

type Writer interface { 
     Write(p []byte) (n int, err error) } 

你看,作者可以处理[]byte作为输入。您可以使用w.Write(gzipFast(&b))而不是fmt.Fprint(w, gzipFast(&b))。那么你就需要取消注释:

//gz := gzip.NewWriter(w) 
//defer gz.Close() 
//json.NewEncoder(gz).Encode(box) 

一切作为一个小例子,这说明什么是你的代码发生的事情:

https://play.golang.org/p/6rzqLWTGiI

我会避免用gzip荷兰国际集团手动[]byte。您可以轻松使用标准库中已有的作家。另外,看看compress/flate,我认为应该使用它来代替gzip。

package main 

import (
    "net/http" 
    "encoding/json" 
    "compress/gzip" 
    "log" 
) 

type Box struct { 
    Width int `json:"width"` 
} 

func writeJsonResponseCompressed(w http.ResponseWriter, r *http.Request) { 

    box := &Box{Width: 10} 

    w.Header().Set("Content-Type", "application/json") 
    w.Header().Set("Content-Encoding", "gzip") 

    body, err := json.Marshal(box) 
    if err != nil { 
     // Your error handling 
     return 
    } 

    writer, err := gzip.NewWriterLevel(w, gzip.BestCompression) 
    if err != nil { 
     // Your error handling 
     return 
    } 

    defer writer.Close() 

    writer.Write(body) 
} 

func main() { 
    http.HandleFunc("/compressedget", writeJsonResponseCompressed) 
    log.Fatal(http.ListenAndServe(":8081", nil)) 
} 
+0

我同意,这对我来说是学习一些东西的考验。如果这个API是生产应用程序,那么你的解决方案是最好的。 – stihl