2016-12-20 25 views
0

我已经在服务器端(golang)创建了pdf,然后我想通过api调用下载该pdf。我已经使用了ajax post请求。该请求直接转入以下ExportReport handlder。但我下载的pdf文档是空白页。 有因为请求头 错误的内容长度设置的错误发生的情况是:PDF下载不在golang服务器端工作

http: wrote more than the declared Content-Length 
2016/12/20 14:37:39 http: multiple response.WriteHeader calls 

此错误细分PDF download.please去,虽然我的代码片段。

func ExportReport(w http.ResponseWriter, r *http.Request) *core_commons.AppError { 

    url := "https://mydomainname/reporting/repository/dashboard.pdf" 

    timeout := time.Duration(5) * time.Second 
    cfg := &tls.Config{ 
     InsecureSkipVerify: true, 
    } 
    transport := &http.Transport{ 
     TLSClientConfig:  cfg, 
     ResponseHeaderTimeout: timeout, 
     Dial: func(network, addr string) (net.Conn, error) { 
      return net.DialTimeout(network, addr, timeout) 
     }, 
     DisableKeepAlives: true, 
    } 

    client := &http.Client{ 
     Transport: transport, 
    } 
    resp, err := client.Get(url) 
    if err != nil { 
     fmt.Println(err) 
    } 
    defer resp.Body.Close() 

    w.Header().Set("Content-Disposition", "attachment; filename=dashboard.pdf") 
    w.Header().Set("Content-Type", r.Header.Get("Content-Type")) 
    w.Header().Set("Content-Length", r.Header.Get("Content-Length")) 

    _, err = io.Copy(w, resp.Body) 
    if err != nil { 
     fmt.Println(err) 
    } 
    return nil 
} 

以下是如何调用ajax请求。

$.ajax({ 
    type: "POST", 
    url: '/reporting/api/report/export', 
    data: JSON.stringify(payload), 
    contentType: 'application/pdf', 
    success: function(response, status, xhr) { 
     // check for a filename 
     var filename = ""; 
     var disposition = xhr.getResponseHeader('Content-Disposition'); 
     if (disposition && disposition.indexOf('attachment') !== -1) { 
      var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/; 
      var matches = filenameRegex.exec(disposition); 
      if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, ''); 
     } 

     var type = xhr.getResponseHeader('Content-Type'); 
     var blob = new Blob([response], { type: type }); 

     if (typeof window.navigator.msSaveBlob !== 'undefined') { 
      // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed." 
      window.navigator.msSaveBlob(blob, filename); 
     } else { 
      var URL = window.URL || window.webkitURL; 
      var downloadUrl = URL.createObjectURL(blob); 

      if (filename) { 
       // use HTML5 a[download] attribute to specify filename 
       var a = document.createElement("a"); 
       // safari doesn't support this yet 
       if (typeof a.download === 'undefined') { 
        window.location = downloadUrl; 
       } else { 
        a.href = downloadUrl; 
        a.download = filename; 
        document.body.appendChild(a); 
        a.click(); 
       } 
      } else { 
       window.location = downloadUrl; 
      } 

      setTimeout(function() { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup 
     } 
    } 
}); 
+0

无法保证您调用的URL将设置“Content-Length”,因此如果它不为零,则只应在响应中设置它。 – icza

回答

2

看看这两条线:

w.Header().Set("Content-Type", r.Header.Get("Content-Type")) 
w.Header().Set("Content-Length", r.Header.Get("Content-Length")) 

您想设置相同的内容类型和长度得到PDF时,你得到的,但r请求是请求您相关联的一个服务!它应该是:

w.Header().Set("Content-Type", resp.Header.Get("Content-Type")) 
w.Header().Set("Content-Length", resp.Header.Get("Content-Length")) 

而且还指出,也不能保证你调用的URL将设置Content-Length,所以如果它是非零您应该只将其设置成你的回应。另外请注意,它也不能保证它发送的内容长度是正确的,所以你应该小心处理。另请注意,内容长度标题由net/http包自动分析并存储在响应中,您可以使用:Response.ContentLength

如果设置内容长度,则net/http程序包将不允许发送比指定更多的字节。尝试多写给你错误:

http: wrote more than the declared Content-Length

这个小例子证明/验证它:

func h(w http.ResponseWriter, r *http.Request) { 
    w.Header().Set("Content-Length", "1") 
    fmt.Println(w.Write([]byte("hi"))) 
} 

func main() { 
    http.HandleFunc("/", h) 
    panic(http.ListenAndServe(":8080", nil)) 
} 

处理程序h()写入2个字节,但表示只有1个内容的长度。如果您将其更改为2,则一切正常。

那么你应该做的是首先检查r.Header.Get("Content-Length")如果它不是空的string并且是大于0的数字;如果是的话,只设置它。

如果接收到的内容长度丢失,并且您仍然希望在响应中指明它,那么您别无选择,只能先将内容读取到缓冲区中,缓冲区的长度可以在发送前检查并设置。

此外,如果HTTP GET请求成功,您也可以省略检查。您的评论表明这是一个错误页面。首先检查:

resp, err := client.Get(url) 
if err != nil { 
    fmt.Println(err) 
    http.Error(w, "Can't serve PDF.", http.StatusInternalServerError) 
    return 
} 
defer resp.Body.Close() 

if resp.StatusCode != http.StatusOK { 
    http.Error(w, "Can't serve PDF.", http.StatusInternalServerError) 
    return 
} 
+0

r.Header.Get(“Content-Length”)不为空,它给出值434在我的情况下 –

+0

@SandunPriyanka然后它看起来像你的查询返回一个错误页面,这不是PDF。你应该先检查一下。 – icza

+0

以我的情况为例。StatusCode == http.StatusOK但是当运行io.copy时发生上面的错误“http:写的超过声明的内容长度 2016/12/20 15:29:02 http:multiple response.WriteHeader calls” –