2016-12-20 4 views
0

Я создал pdf на serveride (golang), тогда я хочу загрузить этот pdf tough api call. Я использовал запрос ajax post. этот запрос направляется в следующий рабочий стол ExportReport. но мой загруженный pdf-документ - пустая страница. Существует ошибка произойти из-за установки Content-Length в заголовке запроса Ошибка:Загрузка PDF не работает с serveride в 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

Посмотрите на эти 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: написал больше, чем объявленный Content-Length 2016/12/20 15:29:02 http: multiple response.WriteHeader называет« –