Javascript – Download in browser with ajax request

ajaxangularjsfpdfjavascriptphp

I'm working with AngularJS and I'm trying to generate a PDF in php. This is what I have in my 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");
        });
};

In my php file I have the following to create a PDF with FPDF library:

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

But the request is responding this instead of open a save dialog to save my pdf.

%PDF-1.3
3 0 obj
<>
endobj
4 0 obj
<>
stream
x3Rðâ2Ð35W(çr
QÐw3T04Ó30PISp
êZ*[¤(hx¤æää+çå¤(j*dÔ7W
endstream
endobj
1 0 obj
<
endobj
5 0 obj
<
endobj
2 0 obj
<<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
/Font <<
/F1 5 0 R
>
/XObject <<
>
>
endobj
6 0 obj
<<
/Producer (FPDF 1.7)
/CreationDate (D:20150611094522)
>
endobj
7 0 obj
<<
/Type /Catalog
/Pages 1 0 R
>
endobj
xref
0 8
0000000000 65535 f
0000000228 00000 n
0000000416 00000 n
0000000009 00000 n
0000000087 00000 n
0000000315 00000 n
0000000520 00000 n
0000000595 00000 n
trailer
<<
/Size 8
/Root 7 0 R
/Info 6 0 R
>
startxref
644
%%EOF

I've used the PHPExcel library and this worked:

$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');

Now how can I make this work for my PDF?

UPDATE:

I've edited my code to this:

$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);

The file is saved locally but the download doesn't work. It doesn't get my a dialog to save the pdf. What am I doing wrong?

UPDATE 2

I've now tried to change application-download in application/pdf. He saves the file locally but I don't get a download dialog box.

The response looks like this (when I check Network in Chrome):

%PDF-1.3
3 0 obj
<>
endobj
4 0 obj
<>
stream
x3Rðâ2Ð35W(çr
QÐw3T04Ó30PISp
êZ*[¤(hx¤æää+çå¤(j*dÔ7W
endstream
endobj
1 0 obj
<
endobj
5 0 obj
<
endobj
2 0 obj
<<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
/Font <<
/F1 5 0 R
>
/XObject <<
>
>
endobj
6 0 obj
<<
/Producer (FPDF 1.7)
/CreationDate (D:20150617090832)
>
endobj
7 0 obj
<<
/Type /Catalog
/Pages 1 0 R
>
endobj
xref
0 8
0000000000 65535 f
0000000228 00000 n
0000000416 00000 n
0000000009 00000 n
0000000087 00000 n
0000000315 00000 n
0000000520 00000 n
0000000595 00000 n
trailer
<<
/Size 8
/Root 7 0 R
/Info 6 0 R
>
startxref
644
%%EOF

Best Solution

UPDATE

The method of using FileSaver.js also doesn't work , there seems to be no way to forcefully invoke the native Save As Dialog via JavaScript alone , only exception being the saveAs execCommand for IE. Check Does execCommand SaveAs work in Firefox?

Include FileSaver.js as dependency in your project

Change the downloadPDF function as follows

 $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");
      });

 };

The Blob object will work in most modern browser but there is limited support for IE < 10 , check https://github.com/eligrey/FileSaver.js#supported-browsers for polyfills

Also remove Content-Disposition: attachment and Content-Type: application-download headers as we don't want browser to natively handle the download process

Here is a working demo http://plnkr.co/edit/9r1tehQ94t5vkqZfZuQC?p=preview , it shows GET request to a PDF

Related Question