我在serverside(golang)中创建了pdf,然后我想通过api调用下载该pdf。我使用了ajax post请求。 该请求直接进入随后的
由于请求标头上的Content-Length设置而发生错误
错误是:
1 2 | http: wrote more than the declared Content-Length 2016/12/20 14:37:39 http: multiple response.WriteHeader calls |
这个错误分解为pdf download.please通过我的代码片段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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请求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | $.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[^;=\ ]*=((['"]).*?\\2|[^;\ ]*)/; 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 } } }); |
-
无法保证您调用的URL会设置
Content-Length ,因此,如果响应的URL非零,则只应在响应中进行设置。
查看以下两行:
1 2 | w.Header().Set("Content-Type", r.Header.Get("Content-Type")) w.Header().Set("Content-Length", r.Header.Get("Content-Length")) |
您希望设置与获取PDF时相同的内容类型和长度,但是
1 2 | w.Header().Set("Content-Type", resp.Header.Get("Content-Type")) w.Header().Set("Content-Length", resp.Header.Get("Content-Length")) |
还要注意,不能保证您调用的URL会设置
如果设置内容长度,则
http: wrote more than the declared Content-Length
这个小例子证明/验证了它:
1 2 3 4 5 6 7 8 9 | 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)) } |
处理程序
因此,您应该首先检查
如果缺少接收到的内容长度,并且您仍然希望在响应中指出该内容,那么您别无选择,只能将内容读入缓冲区,在发送之前可以检查并设置其长度。
同样,您忽略了
1 2 3 4 5 6 7 8 9 10 11 12 | 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 } |
- r.Header.Get(" Content-Length")不为空,在我的情况下为434
- @SandunPriyanka然后,您的查询似乎返回一个错误页面,该页面不是PDF。 您应该先检查一下。
- 在我的情况下,resp.StatusCode == http.StatusOK,但是在运行io.copy时,发生了以上错误" http:写的内容多于声明的Content-Length 2016/12/20 15:29:02 http:多个response.WriteHeader 电话"
-
@SandunPriyanka参见编辑后的答案。 您使用了
r.Header.Get("Content-Type") ,但您应该使用resp.Header.Get("Content-Type") 。 - 该错误已修复,但生成的pdf为空白页。
- @SandunPriyanka林不惊讶PDF是空白的。 一个PDF有一个标题,如果完整的PDF只有434个字节,那么内容的空间就不大了。 您的PDF源不生成内容。
- 感谢你的帮助。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | package main import ( "encoding/base64" "fmt" "io" "net/http" "net/url" "path" ) func main() { fmt.Println("Starting transform download sever at http://127.0.0.1:2333") http.HandleFunc("/", HandleClient) err := http.ListenAndServe(":2333", nil) if err != nil { fmt.Println(err) } } func HandleClient(writer http.ResponseWriter, request *http.Request) { //First of check if Get is set in the URL encoded := request.URL.Query().Get("url") if encoded =="" { //Get not set, send a 400 bad request http.Error(writer,"Get 'url' not specified in url.", 500) return } decoded, err := base64.StdEncoding.DecodeString(encoded) if err != nil { http.Error(writer,"base64 decode error", 501) return } fileUrl := string(decoded) filename, err := GetFilename(fileUrl) if err != nil { http.Error(writer,"error url", 502) return } resp, err := http.Get(fileUrl) if err != nil { http.Error(writer,"error url", 502) return } defer resp.Body.Close() writer.Header().Set("Content-Disposition","attachment; filename="+filename) writer.Header().Set("Content-Type", resp.Header.Get("Content-Type")) writer.Header().Set("Content-Length", resp.Header.Get("Content-Length")) _, err = io.Copy(writer, resp.Body) if err != nil { http.Error(writer,"Remote server error", 503) return } return } func GetFilename(inputUrl string) (string, error) { u, err := url.Parse(inputUrl) if err != nil { return"", err } u.RawQuery ="" return path.Base(u.String()), nil } |
使用像http://127.0.0.1:2333/?url=
- 欢迎来到SO! 您是否也想通过添加一些解释来改善答案?
- @FailedScientist您可以在终端上构建并运行它以启动服务器