Home » Php » javascript – Download in browser with ajax request

javascript – Download in browser with ajax request

Posted by: admin April 23, 2020 Leave a comment

Questions:

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

How to&Answers:

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

Answer:

Let me suggest you one thing.

By AJAX only you cannot download any file. You need to create .zip file first with ajax call and after that gaining that file path you have to open that file using any command.

Here one example that works for me: 🙂

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

Surely, It works. Try once. 🙂

EDIT:

Or you can use

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

in ‘abc.com/link’ :
use below code:

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

You will get your file in download-list.

Answer:

According to the documentation you can set the 2nd parameter to D:

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

If you want to handle all the details yourself using a saved file, this is how I’ve presented PDFs to the browser for download from 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);

Update:

Please verify that the PDF file is actually being made. Does the web server process have write access to that path? Is FPDF being included properly? I made this on a test VM and it presented a file 1.pdf for download, which opened and contained “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);

?>

Answer:

With ajax it is not possible.
Instead what you can do is, hit your action through javascript form submit.

e.g., document.getElementById(formID).action= actionName.

document.getElementById(formID).submit();  

Here the formID is your form’s id and the actionName will be '/download-pdf' as you have specified.

In your action class – set response headers

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

This will cause your file to be downloaded with filename = pdffile.pdf.

Answer:

header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . basename($file_url) . "\"");

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

 };

Or create a element with download attribute:

$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

Answer:

This is not possible with (only) AJAX.

There are two main techniques you could use here, both without using JavaScript to perform the actual download. The general idea is that you redirect the browser to a resource that will provide the desired document; if you’re redirecting to a download it will not leave the page but show the Save dialog instead. A similar topic is discussed in this question.

GET Request

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

Adjust your server-side script to use $_GET['fid'].

POST Request

The server creates a resource and responds with a URL where the resource can be found:

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

Adjust your server-side script to return an appropriate JSON response after creating the new resource.

Answer:

Send the document to a given destination: browser, file or string. In the case of browser, the plug-in may be used (if present) or a download (“Save as” dialog box) may be forced.

The method first calls Close() if necessary to terminate the document.

Parameters

name:

The name of the file. If not specified, the document will be sent to the browser (destination I) with the name doc.pdf.

dest

Destination where to send the document. It can take one of the following values:

  • I: send the file inline to the browser. The plug-in is used if
    available. The name given by name is used when one selects the “Save
    as” option on the link generating the PDF.
  • D: send to the browser and force a file download with the name given
    by name.
  • F: save to a local file with the name given by name (may
    include a path).
  • S: return the document as a string. name is ignored.