2016-12-20 1 views
0

Ich habe pdf in serverside (golang) erstellt dann möchte ich das pdf herunterladen thorugh der api call.I habe ajax post-anfrage verwendet. Diese Anfrage direkt in folgenden ExportReport Handler. aber mein heruntergeladenes PDF-Dokument ist eine leere Seite. Es gibt Fehler passieren, weil der Content-Length-Einstellung auf Anfrage Kopf Fehler ist:PDF download funktioniert nicht von serverside in golang

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

Dieser Fehler aufgeschlüsselt pdf download.please gehen, obwohl mein Code-Schnipsel.

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 
} 

Nachfolgend finden Sie, wie Sie eine Ajax-Anfrage aufrufen.

$.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

Es gibt keine Garantie dafür, dass die URL, die Sie gesetzt rufen Sie die ' 'Content-Length', und so sollten Sie nur setzen Sie ihn in Ihrer Antwort, wenn es nicht Null ist. – icza

Antwort

2

Sehen sie sich diese zwei Linien:

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

Sie wollen den gleichen Inhaltstyp festlegen und Länge erhalten Sie, wenn Sie die PDF bekommen, aber die r Anfrage ist die mit der Anforderung verbundenen Sie servieren Sie! Es sollte sein:

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

Und beachten Sie auch, dass es keine Garantie dafür gibt, dass die URL Sie die Content-Length gesetzt nennen, und so sollten Sie es nur in Ihrer Antwort gesetzt, wenn es nicht Null ist. Beachten Sie auch, dass es keine Garantie gibt, dass die Länge des Inhalts korrekt ist. Sie sollten daher vorsichtig damit umgehen. Beachten Sie auch, dass die Kopfzeile für die Inhaltslänge automatisch vom net/http-Paket analysiert wird und in der Antwort gespeichert wird. Sie können genau das verwenden: .

Wenn Sie die Inhaltslänge festlegen, können Sie mit dem net/http-Paket nicht mehr Bytes als angegeben senden. Der Versuch, mehr zu schreiben gibt Ihnen den Fehler:

http: wrote more than the declared Content-Length

Dieses kleine Beispiel zeigt/prüft es:

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

Der Handler h() schreibt 2 Bytes, sondern zeigt nur 1 in Inhaltslänge. Wenn Sie das auf 2 ändern, funktioniert alles.

Also was Sie tun sollten, ist zuerst zu überprüfen r.Header.Get("Content-Length"), wenn es nicht die leere string ist und eine Zahl größer als 0; und setze es nur wenn es so ist.

Wenn die Länge des empfangenen Inhalts fehlt und Sie dies immer noch in Ihrer Antwort angeben möchten, haben Sie keine andere Wahl, als zuerst den Inhalt in einen Puffer einzulesen, dessen Länge Sie vor dem Senden überprüfen und einstellen können.

Auch Sie nicht überprüfen, ob die HTTP GET Anfrage erfolgreich war. Ihr Kommentar zeigt an, dass es sich um eine Fehlerseite handelt. Überprüfen Sie, ob zuerst:

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") ist nicht leer es gibt Wert 434 in meinem Fall –

+0

@SandunPriyanka Dann sieht es aus wie Ihre Abfrage gibt eine Fehlerseite zurück, die nicht PDF ist. Sie sollten das zuerst überprüfen. – icza

+0

In meinem Fall resp.StatusCode == http.StatusOK aber beim Ausführen th io.copy gibt es oben genannten Fehler "http: schrieb mehr als die angegebene Content-Länge 2016/12/20 15:29:02 http: multiple response.WriteHeader ruft" –