【问题标题】:Download in browser with ajax request使用ajax请求在浏览器中下载
【发布时间】:2015-08-26 22:33:13
【问题描述】:

我正在使用 AngularJS,我正在尝试在 php 中生成 PDF。这就是我的 controller.js 中的内容:

$scope.downloadPDF = function(){
    $http.post('/download-pdf', { fid: $routeParams.fid })
        .success(function(data, status, headers, config){
            console.log("success");
        })
        .error(function(data, status, headers, config){
            console.log("error");
        });
};

在我的 php 文件中,我有以下内容可以使用FPDF library 创建 PDF:

function download_pdf()
{
    $id = $_POST['fid'];

    $pdf = new FPDF();

    $pdf->AddPage();
    $pdf->SetFont('Arial','B',16);
    $pdf->Cell(40,10,'Hello World!');

    header('Content-Type: application/pdf');
    header('Content-Disposition: attachment; filename=' . $id . '.pdf');

    $pdf->Output('Order123.pdf', 'D');
}

但是请求是响应这个而不是打开保存对话框来保存我的 pdf。

%PDF-1.3 3 0 对象 结束对象 4 0 对象 溪流 x3Rðâ2Ð35W(çr QÐw3T04Ó30PISp êZ*[¤(hx¤æää+çå¤(j*dÔ7W 端流 结束对象 1 0 对象 /X 对象 > 结束对象 6 0 对象 结束对象 7 0 对象 结束对象 外部参照 0 8 0000000000 65535 f 0000000228 00000 0000000416 00000 0000000009 00000 n 0000000087 00000 0000000315 00000 0000000520 00000 0000000595 00000 预告片 起始外部参照 644 %%EOF

我已经使用了PHPExcel 库,这很有效:

$objWriter = PHPExcel_IOFactory::createWriter($ea, 'Excel2007');

// We'll be outputting an excel file
header('Content-type: application/vnd.ms-excel');

// It will be called Submission on [date_now].xls
header('Content-Disposition: attachment; filename="' . $filename . '.xls' . '"');

// Write file to the browser
$objWriter->save('php://output');

现在我怎样才能使它适用于我的 PDF?

更新:

我已将我的代码编辑为:

$pdf = new FPDF();

$pdf->AddPage();
$pdf->SetFont('Arial','B',16);
$pdf->Cell(40,10,'Hello World!');

$filename = DXS_VKGROUP_PLUGIN_LIB_DIR . 'uploads/' . $_POST['fid'] . '.pdf';

$pdf->Output($filename, 'F'); // Save file locally

header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Content-Type: application-download');
header('Content-Length: ' . filesize($filename));
header('Content-Transfer-Encoding: binary');
header('Content-Disposition: attachment; filename="' . $filename . '"');

$handle = fopen($filename, 'rb');
fpassthru($handle);
fclose($handle);

文件保存在本地,但无法下载。它没有让我的对话框保存 pdf。我做错了什么?

更新 2

我现在尝试将application-download 更改为application/pdf。他将文件保存在本地,但我没有得到下载对话框。

响应看起来像这样(当我在 Chrome 中检查 Network 时):

%PDF-1.3 3 0 对象 结束对象 4 0 对象 溪流 x3Rðâ2Ð35W(çr QÐw3T04Ó30PISp êZ*[¤(hx¤æää+çå¤(j*dÔ7W 端流 结束对象 1 0 对象 /X 对象 > 结束对象 6 0 对象 结束对象 7 0 对象 结束对象 外部参照 0 8 0000000000 65535 f 0000000228 00000 0000000416 00000 0000000009 00000 n 0000000087 00000 0000000315 00000 0000000520 00000 0000000595 00000 预告片 起始外部参照 644 %%EOF

【问题讨论】:

  • 如果您的网络浏览器设置没有勾选“下载前询问每个文件的保存位置”(Chrome)选项或类似选项(Firefox - 始终询问保存文件的位置),那么浏览器将不会即使您将文件的标题设置为 application-downloadContent-Disposition: attachment ,也不会显示“另存为”对话框。该文件将自动开始下载。在这种情况下,github.com/eligrey/FileSaver.js 之类的东西可能会有所帮助
  • 如果你使用header('Content-Type: application/octet-stream');而不是application/pdf会发生什么?
  • @Darren 我检查了,这也导致文件下载过程自动启动而没有任何保存提示
  • 您尝试了吗:thisthis
  • 你为什么让赏金到期?没有一个答案有用,还是...?

标签: javascript php ajax angularjs fpdf


【解决方案1】:

我是按以下方式完成的,它对我很有效:

$(document).ready(function () {
            $('#generaPdf').click(function (e) {
            e.preventDefault();
            var name = $('#name').val();
            $.ajax
                ({
                    type: "POST",
                    url: "pdf.php",
                    data: { "name": name },
                    success: function (data) {
                        alert("todo ok")
                        location.href = "pdf.php"
                        // $('.result').html(data);
                        // $('#contactform')[0].reset();
                    }
                });
            });
        });

我的html:

<button type="submit" class="btn btn-primary" id="generaPdf"><i class="fas fa-link"></i> Generar documento para impresión</button>

【讨论】:

    【解决方案2】:

    让我建议你一件事。

    仅通过 AJAX 您无法下载任何文件。 您需要先使用 ajax 调用创建 .zip 文件,然后获得该文件路径,然后您必须使用任何命令打开该文件。

    这里有一个对我有用的例子:)

    $http.post('www.abc.com/zipcreate', {'id': id}).then(function (zipurl) {
                        $window.location = zipurl;
                });
    

    当然,它有效。试一次。 :)

    编辑:

    或者你可以使用

    $window.location = 'abc.com/link';
    

    在“abc.com/link”中: 使用下面的代码:

      header("Content-type: application/octet-stream");
      header("Content-Disposition: attachment; filename=".$name);
      header("Pragma: no-cache");
      header("Expires: 0");
      readfile($file);
      exit;
    

    您将在下载列表中获得您的文件。

    【讨论】:

    • 他们为什么需要创建一个 zip 文件?这不会让用户更难实际打开文档吗?
    【解决方案3】:

    这对于(仅)AJAX 是不可能的。

    您可以在此处使用两种主要技术,均不使用使用 JavaScript 来执行实际下载。一般的想法是您将浏览器重定向到将提供所需文档的资源;如果您要重定向到下载,它不会离开页面,而是显示“保存”对话框。 this question 中讨论了类似的主题。

    GET 请求

    location.href = '/download-pdf?fid=' + encodeURIComponent($routeParams.fid);
    

    调整您的服务器端脚本以使用$_GET['fid']

    POST 请求

    服务器创建一个资源并返回一个可以找到该资源的 URL:

    $http.post('/download-pdf', { fid: $routeParams.fid })
        .success(function(data, status, headers, config) {
            // follow location provided by server
            location.href = data.redirect_url;
            console.log("success");
        })
        .error(function(data, status, headers, config){
            console.log("error");
        });
    

    调整您的服务器端脚本以在创建新资源后返回适当的 JSON 响应。

    【讨论】:

      【解决方案4】:

      更新

      使用 FileSaver.js 的方法也不起作用,似乎没有办法仅通过 JavaScript 强制调用本机另存为对话框,唯一的例外是 IE 的 saveAs execCommand。检查Does execCommand SaveAs work in Firefox?

      在你的项目中包含FileSaver.js作为依赖项

      修改downloadPDF函数如下

       $scope.downloadPDF = function() {
          $http.post('/download-pdf', {
              fid: $routeParams.fid
            }, {
              responseType: 'arraybuffer'
            })
            .success(function(data, status, headers, config) {
              // Convert response data to Blob
              var file = new Blob([data], {
                type: 'application/pdf'
              });
              // Call the File Saver method to save the above Blob,this will show Save As dialog
              saveAs(file, "test.pdf");
            })
            .error(function(data, status, headers, config) {
              console.log("error");
            });
      
       };
      

      Blob 对象可以在大多数现代浏览器中工作,但对 IE https://github.com/eligrey/FileSaver.js#supported-browsers 以获取 polyfills

      同时删除 Content-Disposition: attachmentContent-Type: application-download 标头,因为我们不希望浏览器原生处理下载过程

      这是一个工作演示 http://plnkr.co/edit/9r1tehQ94t5vkqZfZuQC?p=preview,它显示了对 PDF 的 GET 请求

      【讨论】:

      • 试了demo:Chrome没有另存为,直接下载即可
      • 这个答案只是提到了什么不起作用,为什么它仍然得到这么多的赞成票?
      【解决方案5】:

      将文档发送到给定的目的地:浏览器、文件或字符串。在浏览器的情况下,可以使用插件(如果存在)或强制下载(“另存为”对话框)。

      如果需要终止文档,该方法首先调用 Close()。

      参数

      姓名:

      文件的名称。如果未指定,文档将被发送到名为 doc.pdf 的浏览器(目标 I)。

      目的地

      发送文档的目的地。它可以采用以下值之一:

      • 我: 将文件内联发送到浏览器。如果使用该插件 可用的。选择“保存”时使用由名称给出的名称 as”选项在生成 PDF 的链接上。
      • D: 发送到浏览器并强制下载指定名称的文件 按名字。
      • F: 以 name 给出的名称保存到本地文件(可能 包括路径)。
      • S: 将文档作为字符串返回。名称被忽略。

      【讨论】:

        【解决方案6】:
        header('Content-Type: application/octet-stream');
        header("Content-Transfer-Encoding: Binary");
        header("Content-disposition: attachment; filename=\"" . basename($file_url) . "\"");
        

        或使用 BLOB:

         $scope.downloadPDF = function() {
            $http.post('/download-pdf', {
                fid: $routeParams.fid
              }, {
                responseType: 'arraybuffer'
              })
              .success(function(data, status, headers, config) {
        
                var file = new Blob([data], {
                  type: 'application/pdf'
                });
                var url = URL.createObjectURL(blob);
                window.location.href = url;
              })
              .error(function(data, status, headers, config) {
                console.log("error");
              });
        
         };
        

        或者创建一个带有download属性的元素:

        $scope.downloadPDF = function() {
            $http.get('http://46.101.139.188/demo.pdf', {
              }, {
                responseType: 'arraybuffer'
              })
              .success(function(data, status, headers, config) {
        
                var file = new Blob([data], {
                  type: 'application/pdf'
                });
                var url = URL.createObjectURL(file);
                var a = document.createElement('a');
                a.setAttribute('download', 'download');
                a.setAttribute('href', url);
        
                 var event = new MouseEvent('click', {
                  'view': window,
                  'bubbles': true,
                  'cancelable': true
                });
                a.dispatchEvent(event);
              })
              .error(function(data, status, headers, config) {
                console.log("error");
              });
         };
        

        plnkr demo

        【讨论】:

        • 对于大的 PDF 文档,URL 会不会太长?你测试了吗?
        【解决方案7】:

        使用 ajax 是不可能的。
        相反,您可以做的是通过 javascript 表单提交来执行您的操作。

        例如,document.getElementById(formID).action= actionName

        document.getElementById(formID).submit();  
        

        这里的 formID 是您的表单 ID,actionName 将是您指定的 '/download-pdf'

        在您的操作类中 - 设置响应标头

        getResponse().setContentType("application/pdf");
        getResponse().setHeader("Content-Disposition",  " attachment; filename= "+"pdffile.pdf");
        

        这将导致您的文件以 filename = pdffile.pdf 下载。

        【讨论】:

          【解决方案8】:

          根据documentation可以设置第二个参数为D

          $pdf->Output('Order123.pdf', 'D');
          

          如果您想使用保存的文件自己处理所有细节,这就是我将 PDF 呈现给浏览器以从 PHP 下载的方式:

          header('Pragma: public');
          header('Expires: 0');
          header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
          header('Content-Type: application-download');
          header('Content-Length: ' . filesize('file.pdf'));
          header('Content-Transfer-Encoding: binary');
          header('Content-Disposition: attachment; filename="file.pdf"');
          $handle = fopen('file.pdf', 'rb');
          fpassthru($handle);
          fclose($handle);
          

          更新:

          请确认 PDF 文件确实正在制作中。 Web 服务器进程是否对该路径具有写入权限? FPDF 是否被正确包含?我在一个测试虚拟机上做了这个,它提供了一个文件1.pdf 供下载,它打开并包含“Hello World!”:

          <?php
          
          require('/var/www/html/fpdf17/fpdf.php');
          
          $_POST = array('fid' => '1');
          
          $pdf = new FPDF();
          
          $pdf->AddPage();
          $pdf->SetFont('Arial', 'B', 16);
          $pdf->Cell(40, 10, 'Hello World!');
          
          $filename = '/tmp/' . $_POST['fid'] . '.pdf';
          
          $pdf->Output($filename, 'F'); // Save file locally
          
          header('Pragma: public');
          header('Expires: 0');
          header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
          header('Content-Type: application-download');
          header('Content-Length: ' . filesize($filename));
          header('Content-Transfer-Encoding: binary');
          header('Content-Disposition: attachment; filename="' . basename($filename) . '"');
          
          $handle = fopen($filename, 'rb');
          fpassthru($handle);
          fclose($handle);
          
          unlink($filename);
          
          ?>
          

          【讨论】:

          • 我试过D参数,我的题目错了。但仍然输出与主题中所述相同的内容。
          • 在这种情况下,尝试使用F 参数保存本地文件,然后按照我展示的方式将该文件传递给浏览器。最后您可以使用unlink($filename); 将其删除。
          • 尝试header('Content-Disposition: attachment; filename="' . basename($filename) . '"');,因为您不能使用具有完整文件夹路径的文件名。您是否在该代码之前向浏览器发送任何标头?
          • 还是一样的结果。不,我之前没有向浏览器发送任何标题.. 在 firefox/chrome 中尝试过
          • 文件确实被保存了,我可以看到并打开它(当我没有取消链接时)。但是浏览器没有打开下载对话框... .
          猜你喜欢
          • 2017-11-11
          • 1970-01-01
          • 2010-09-17
          • 2012-05-10
          • 2012-04-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-14
          相关资源
          最近更新 更多