【问题标题】:Download and open PDF file using Ajax使用 Ajax 下载并打开 PDF 文件
【发布时间】:2011-01-01 06:13:24
【问题描述】:

我有一个生成 PDF 的操作类。 contentType 设置得当。

public class MyAction extends ActionSupport 
{
   public String execute() {
    ...
    ...
    File report = signedPdfExporter.generateReport(xyzData, props);

    inputStream = new FileInputStream(report);
    contentDisposition = "attachment=\"" + report.getName() + "\"";
    contentType = "application/pdf";
    return SUCCESS;
   }
}

我通过 Ajax 调用将此称为 action。我不知道如何将此流传送到浏览器。我尝试了一些方法,但没有任何效果。

$.ajax({
    type: "POST",
    url: url,
    data: wireIdList,
    cache: false,
    success: function(response)
    {
        alert('got response');
        window.open(response);
    },
    error: function (XMLHttpRequest, textStatus, errorThrown) 
    {
        alert('Error occurred while opening fax template' 
              + getAjaxErrorString(textStatus, errorThrown));
    }
});

上面给出了错误:

您的浏览器发送了此服务器无法理解的请求。

【问题讨论】:

    标签: javascript java jquery pdf


    【解决方案1】:

    这就是我的工作方式

    $.ajax({
      url: '<URL_TO_FILE>',
      success: function(data) {
        var blob=new Blob([data]);
        var link=document.createElement('a');
        link.href=window.URL.createObjectURL(blob);
        link.download="<FILENAME_TO_SAVE_WITH_EXTENSION>";
        link.click();
      }
    });
    &lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"&gt;&lt;/script&gt;

    使用download.js 更新了答案

    $.ajax({
      url: '<URL_TO_FILE>',
      success: download.bind(true, "<FILENAME_TO_SAVE_WITH_EXTENSION>", "<FILE_MIME_TYPE>")
    });

    【讨论】:

    • 它适用于 chrome 吗?我只能看到一个空白的 pdf。
    • 是的,它适用于所有现代浏览器。如果您看到空白 pdf,请尝试在新选项卡中运行 ajax url。如果那里也出现空白屏幕,则可能是 pdf 本身的问题。如果您确实在那里看到了 pdf 文件而不是在下载的文件中,请在我的电子邮件中告诉我。 :)
    • 这个(锚元素)实际上在 IE 11、Edge 和 Firefox 上对我不起作用。将成功更改为仅使用“window.open(URL.createObjectURL(blob))”确实有效。
    • pdf 文件已下载,但没有内容可用。我在服务器端保存了字节 [],并且 pdf 内容可用。请建议。
    • 空白pdf文件已下载。
    【解决方案2】:

    您不一定需要 Ajax。如果您在服务器端代码中将content-disposition 设置为attachment,只需一个&lt;a&gt; 链接就足够了。这样,父页面将保持打开状态,如果这是您主要关心的问题(否则为什么您会不必要地选择 Ajax?)。此外,没有办法很好地异步处理这个问题。 PDF 不是字符数据。是二进制数据。你不能做像$(element).load() 这样的事情。您想为此使用全新请求。因为&lt;a href="pdfservlet/filename.pdf"&gt;pdf&lt;/a&gt; 非常合适。

    为了在服务器端代码方面为您提供更多帮助,您需要详细说明所使用的语言并发布代码尝试的摘录。

    【讨论】:

    • 再一次:您不需要为此需要 Ajax。这只是自找麻烦。 PDF 是二进制数据,而不是 HTML 或 JSON 等字符数据。
    • var url = contextPath + "/xyz/blahBlah.action";网址 += 网址 + “?” +参数;尝试 { var child = window.open(url); child.focus(); } 捕捉 (e) { }
    • 在某些浏览器中,window.open 将保持打开和空白,这可能会让最终用户感到恼火。因此,也不要为此使用 window.open。如果content-disposition 设置为attachment,您将只得到Save as 对话。父页面将保持不变。只需&lt;a href="pdfservlet/filename.pdf"&gt;pdf&lt;/a&gt;&lt;form action="pdfservlet/filename.pdf"&gt;&lt;input type="submit"&gt;&lt;/form&gt; 就足够了。
    • 网址长度有限。并且作者正在询问关于 POST 的问题。
    • 同意@EdwardOlamisan,这不是一个正确的答案,因为作者试图POST 数据。
    【解决方案3】:

    我真的认为过去的任何答案都没有发现原始海报的问题。当发布者尝试 POST 数据并获得下载作为响应时,它们都假定 GET 请求。

    在寻找更好答案的过程中,我们发现了这个jQuery Plugin for Requesting Ajax-like File Downloads

    在其“核心”中,它创建了一个“临时”HTML 表单,其中包含作为输入字段的给定数据。此表单将附加到文档并发布到所需的 URL。之后,表单再次被删除:

    jQuery('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
        .appendTo('body').submit().remove()
    

    更新 与我提到的 jQuery 插件相比,Mayur 的答案看起来很有希望并且非常简单。

    【讨论】:

      【解决方案4】:

      这就是我解决这个问题的方法。
      Jonathan Amend 在this post 上的回答对我帮助很大。
      下面的例子是简化的。

      如需更多详细信息,上述源代码可以使用 JQuery Ajax 请求(GET、POST、PUT 等)下载文件。它还有助于将参数上传为 JSON 并将内容类型更改为 application/json(我的默认值)

      html来源:

      <form method="POST">
          <input type="text" name="startDate"/>
          <input type="text" name="endDate"/>
          <input type="text" name="startDate"/>
          <select name="reportTimeDetail">
              <option value="1">1</option>
          </select>
          <button type="submit"> Submit</button>
      </form>  
      

      一个包含两个输入文本、一个选择和一个按钮元素的简单表单。

      javascript 页面来源:

      <script type="text/javascript" src="JQuery 1.11.0 link"></script>
      <script type="text/javascript">
          // File Download on form submition.
          $(document).on("ready", function(){
              $("form button").on("click", function (event) {
                  event.stopPropagation(); // Do not propagate the event.
      
                  // Create an object that will manage to download the file.
                  new AjaxDownloadFile({
                      url: "url that returns a file",
                      data: JSON.stringify($("form").serializeObject())
                  });
      
                  return false; // Do not submit the form.
              });
          });
      </script>  
      

      按钮点击的简单事件。它创建一个 AjaxDownloadFile 对象。 AjaxDownloadFile 类源代码如下。

      AjaxDownloadFile 类来源:

      var AjaxDownloadFile = function (configurationSettings) {
          // Standard settings.
          this.settings = {
              // JQuery AJAX default attributes.
              url: "",
              type: "POST",
              headers: {
                  "Content-Type": "application/json; charset=UTF-8"
              },
              data: {},
              // Custom events.
              onSuccessStart: function (response, status, xhr, self) {
              },
              onSuccessFinish: function (response, status, xhr, self, filename) {
              },
              onErrorOccured: function (response, status, xhr, self) {
              }
          };
          this.download = function () {
              var self = this;
              $.ajax({
                  type: this.settings.type,
                  url: this.settings.url,
                  headers: this.settings.headers,
                  data: this.settings.data,
                  success: function (response, status, xhr) {
                      // Start custom event.
                      self.settings.onSuccessStart(response, status, xhr, self);
      
                      // Check if a filename is existing on the response headers.
                      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
                      }
      
                      // Final custom event.
                      self.settings.onSuccessFinish(response, status, xhr, self, filename);
                  },
                  error: function (response, status, xhr) {
                      // Custom event to handle the error.
                      self.settings.onErrorOccured(response, status, xhr, self);
                  }
              });
          };
          // Constructor.
          {
              // Merge settings.
              $.extend(this.settings, configurationSettings);
              // Make the request.
              this.download();
          }
      };
      

      我创建了这个类来添加到我的 JS 库中。它是可重复使用的。希望对您有所帮助。

      【讨论】:

      • Blob 对象在 IE10+ 中被支持。
      • 我必须将 xhr 的 responseType 设置为 arraybufferblob 才能正常工作。 (否则,这很好用。)
      • 我也有同样的问题。所有回应“只是让它成为一个链接”的人都对 OP 没有帮助。如果您的内容是动态的并且您要访问的链接是动态的,那么您必须全部查询...我的答案是在页面上放置一个包含所有隐藏输入的表单(不向用户显示)和然后填写并用jquery提交。效果很好。
      • 这是一个很好的答案,但由于某种原因,我不断收到损坏的空 PDF。想不通。当我通过 API 返回相同的字节集时 - 很好,所以这与 MVC 响应有关。我使用 FileResult 响应类型:File(bytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
      • 澄清:如果通过地址栏打开 URL - 文件被正确打开。如果我使用 AJAX + blob 来获取文件 - 文件格式错误。
      【解决方案5】:

      对我有用的是以下代码,因为服务器函数正在检索File(memoryStream.GetBuffer(), "application/pdf", "fileName.pdf");:

      $http.get( fullUrl, { responseType: 'arraybuffer' })
                  .success(function (response) {
                      var blob = new Blob([response], { type: 'application/pdf' });
      
                      if (window.navigator && window.navigator.msSaveOrOpenBlob) {
                          window.navigator.msSaveOrOpenBlob(blob); // for IE
                      }
                      else {
                          var fileURL = URL.createObjectURL(blob);
                          var newWin = window.open(fileURL);
                          newWin.focus();
                          newWin.reload();
                      }
      });
      

      【讨论】:

      • 在发表此评论和最新的 Chrome 时,这对我来说非常有效
      【解决方案6】:

      你可以使用这个插件来创建一个表单,提交它,然后从页面中删除它。

      jQuery.download = function(url, data, method) {
          //url and data options required
          if (url && data) {
              //data can be string of parameters or array/object
              data = typeof data == 'string' ? data : jQuery.param(data);
              //split params into form inputs
              var inputs = '';
              jQuery.each(data.split('&'), function() {
                  var pair = this.split('=');
                  inputs += '<input type="hidden" name="' + pair[0] +
                      '" value="' + pair[1] + '" />';
              });
              //send request
              jQuery('<form action="' + url +
                      '" method="' + (method || 'post') + '">' + inputs + '</form>')
                  .appendTo('body').submit().remove();
          };
      };
      
      
      $.download(
          '/export.php',
          'filename=mySpreadsheet&format=xls&content=' + spreadsheetData
      );
      

      这对我有用。找到这个插件here

      【讨论】:

      • 这个插件只是创建一个表单,提交它,然后从页面中删除它。 (如果有人想知道)
      【解决方案7】:

      关于Mayur Padshala 给出的答案,这是通过 ajax 下载 pdf 文件的正确逻辑,但正如其他人在 cmets 中报告的那样,此解决方案确实是下载空白 pdf。

      这个原因在这个question 的接受答案中得到了解释:jQuery 在使用 AJAX 请求加载二进制数据时存在一些问题,因为它还没有实现一些 HTML5 XHR v2 功能,请参阅这个增强request 和这个discussion.

      所以使用HTMLHTTPRequest 代码应该是这样的:

      var req = new XMLHttpRequest();
      req.open("POST", "URL", true);
      req.responseType = "blob";
      req.onload = function (event) {
          var blob = req.response;
          var link=document.createElement('a');
          link.href=window.URL.createObjectURL(blob);
          link.download="name_for_the_file_to_save_with_extention";
          link.click();
      }
      

      【讨论】:

        【解决方案8】:

        以下代码对我有用

        //Parameter to be passed
        var data = 'reportid=R3823&isSQL=1&filter=[]';
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "Reporting.jsp"); //url.It can pdf file path
        xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xhr.responseType = "blob";
        xhr.onload = function () {
            if (this.status === 200) {
                var blob = new Blob([xhr.response]);
                const url = window.URL.createObjectURL(blob);
                var a = document.createElement('a');
                a.href = url;
                a.download = 'myFile.pdf';
                a.click();
                setTimeout(function () {
                    // For Firefox it is necessary to delay revoking the ObjectURL
                    window.URL.revokeObjectURL(data)
                        , 100
                })
            }
        };
        xhr.send(data);
        

        【讨论】:

          【解决方案9】:

          为了解决post请求中的空白PDF问题以获取PDF等流数据,我们需要在请求中添加响应类型为'arraybuffer'或'blob'

          $.ajax({
            url: '<URL>',
            type: "POST",
            dataType: 'arraybuffer',
            success: function(data) {
              let blob = new Blob([data], {type: 'arraybuffer'});
              let link = document.createElement('a');
              let objectURL = window.URL.createObjectURL(blob);
              link.href = objectURL;
              link.target = '_self';
              link.download = "fileName.pdf";
              (document.body || document.documentElement).appendChild(link);
              link.click();
              setTimeout(()=>{
                  window.URL.revokeObjectURL(objectURL);
                  link.remove();
              }, 100);
            }
          });
          

          【讨论】:

            【解决方案10】:

            希望这可以为您节省几个小时,让您免于头痛。 我花了一些时间才弄清楚这一点,但是执行常规 $.ajax() 请求会破坏我的 PDF 文件,而通过地址栏请求它则效果很好。 解决方案是这样的:

            包括download.js:http://danml.com/download.html

            然后使用 XMLHttpRequest 代替 $.ajax() 请求。

                var ajax = new XMLHttpRequest(); 
            
                ajax.open("GET", '/Admin/GetPdf' + id, true); 
                ajax.onreadystatechange = function(data) { 
                    if (this.readyState == 4)
                    {
                        if (this.status == 200)
                        {
                            download(this.response, "report.pdf", "application/pdf");
            
                        }
                        else if (this.responseText != "")
                        {
                            alert(this.responseText);
                        }
                    }
                    else if (this.readyState == 2)
                    {
                        if (this.status == 200)
                        {
                            this.responseType = "blob";
                        }
                        else
                        {
                            this.responseType = "text";
                        }
                    }
                };
            
                ajax.send(null);
            

            【讨论】:

            • 我在同一个问题上花了几个小时。 PDF 字体将无法正确加载。谢谢!
            【解决方案11】:

            创建一个隐藏的 iframe,然后在上面的 ajax 代码中:

            网址:document.getElementById('myiframeid').src = your_server_side_url,

            并删除window.open(response);

            【讨论】:

            • 这个解决方案就像一个魅力。我正在调用服务器端脚本,该脚本对通过 curl 获取文件的服务进行 curl 调用。这很好用,因为我可以删除加载 gif 并禁用请求链接。
            • 此解决方案适用于 GET 请求,而不适用于原始帖子中的 POST 请求。
            【解决方案12】:

            这个 sn-p 适用于 Angular js 用户,他们将面临同样的问题,请注意,响应文件是使用编程的点击事件下载的。 在这种情况下,标头由服务器发送,包含文件名和内容/类型。

            $http({
                method: 'POST', 
                url: 'DownloadAttachment_URL',
                data: { 'fileRef': 'filename.pdf' }, //I'm sending filename as a param
                headers: { 'Authorization': $localStorage.jwt === undefined ? jwt : $localStorage.jwt },
                responseType: 'arraybuffer',
            }).success(function (data, status, headers, config) {
                headers = headers();
                var filename = headers['x-filename'];
                var contentType = headers['content-type'];
                var linkElement = document.createElement('a');
                try {
                    var blob = new Blob([data], { type: contentType });
                    var url = window.URL.createObjectURL(blob);
            
                    linkElement.setAttribute('href', url);
                    linkElement.setAttribute("download", filename);
            
                    var clickEvent = new MouseEvent("click", {
                        "view": window,
                        "bubbles": true,
                        "cancelable": false
                    });
                    linkElement.dispatchEvent(clickEvent);
                } catch (ex) {
                    console.log(ex);
                }
            }).error(function (data, status, headers, config) {
            }).finally(function () {
            
            });
            

            【讨论】:

            • 请为你的答案写一些解释。
            【解决方案13】:

            var xhr;
            var beforeSend = function(){
                $('#pleasewaitDL').modal('show');
            }
            $(function () {
                $('#print_brochure_link').click(function(){
                    beforeSend();
                    xhr = new XMLHttpRequest();
                    xhr.open("GET",$('#preparedPrintModalForm').attr('action'), true); 
                    xhr.responseType = "blob";
                    xhr.onload = function (e) {
                        if (this.status === 200) {
                            var file = window.URL.createObjectURL(this.response);
                            var a = document.createElement("a");
                            a.href = file;
                            a.download = this.response.name || "Property Brochure";
                            console.log(file);
                            document.body.appendChild(a);
                            a.click();
                            
                            window.onfocus = function () {                     
                              document.body.removeChild(a)
                            }
                            $('#pleasewaitDL').modal('hide');
                        };
                    };
                    xhr.send($('#preparedPrintModalForm').serialize());
                });
                $('#pleasewaitDLCancel').click(function() {
                    xhr.abort();
                });
            });

            【讨论】:

              【解决方案14】:

              你必须用 Ajax 来做吗?不是有可能将其加载到 iframe 中吗?

              【讨论】:

              • 我正在检查是否可以使用 Ajax 来完成。如果这在技术上是不可能的或劣质的方法,我会改用其他方法。
              【解决方案15】:

              如果您必须像我们一样使用文件流(因此没有物理保存的 PDF),并且您想在不重新加载页面的情况下下载 PDF,则以下功能适用于我们:

              HTML

              <div id="download-helper-hidden-container" style="display:none">
                   <form id="download-helper-form" target="pdf-download-output" method="post">
                          <input type="hidden" name="downloadHelperTransferData" id="downloadHelperTransferData" />
                   </form>
                   <iframe id="pdf-helper-output" name="pdf-download-output"></iframe>
              </div>
              

              Javascript

              var form = document.getElementById('download-helper-form');
              $("#downloadHelperTransferData").val(transferData);
              form.action = "ServerSideFunctionWhichWritesPdfBytesToResponse";
              form.submit();
              

              由于target="pdf-download-output",响应被写入iframe,因此不会执行页面重新加载,而是在浏览器中输出pdf-response-stream作为下载。

              【讨论】:

              • 抱歉,请问您如何获取 transferData 值?
              【解决方案16】:

              我找到了一个为我解决了这个问题的解决方案(使用 jquery ajax 时的空白 pdf)。我在这里找到了这个神奇的解决方案:https://www.py4u.net/discuss/904599(答案 2),它涉及将 xhrFields 添加到您的 ajax 调用中:

              xhrFields: {
                 responseType: 'blob'
              }
              

              我的工作示例:

              $.ajax({
                    url: "myUrl",
                    type: 'GET',
                    headers: {"token": mySecurityToken},
                    xhrFields: {
                              responseType: 'blob'
                    },
                    data: {id: myId}
                  }).done(function( data, statusText, xhr ) {
                      var filename = "";
                      var disposition = xhr.getResponseHeader("Content-Disposition");
                      if (disposition && (disposition.indexOf("attachment") !== -1) || disposition.indexOf("filename") !== -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([data], {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
                      }
              
                  })
              

              我希望这能为你们中的许多人解决这个令人讨厌的问题。

              【讨论】:

                猜你喜欢
                • 2017-03-26
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-04-07
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多