Moshe
Moshe

Reputation: 58067

How can I create a download link of a PDF that does not require a right click?

I am working on a website where the visitor should be able to download a pdf file. (There are three links to choose from but that is irrelevant) I wanted to know how to make it so that the visitor can simply click the link and not have to

right click > Save (target) As...

I am open to PHP and or Javascript solutions. Thanks.

EDIT: Can I use javascript to call the PHP and save the file via AJAX?

EDIT2: I used Nirmal's solution in the end, since it was the simplest to change for all three files. I didn't need to make 3 files for the three PDF's and I didn't need to hand code the switch. BalusC gets the check though since his/her code was up first and does the trick too.

Upvotes: 3

Views: 15401

Answers (6)

ksg91
ksg91

Reputation: 1299

If you don't want to mess with server side code and if this is a low priority thing for you, you can use HTML5 download attribute.

Example:

<a href="myfile.pdf" download>Download</a>

However this is not supported by all browser. As per w3schools, following browsers are supported:

Chrome (14+), Firefox(20+), Opera (15+).

You can also specify filename by giving value to download attr:

<a href="myfile.pdf" download="NewName.pdf">Download</a>

Upvotes: 2

BalusC
BalusC

Reputation: 1108567

All you basically need to do is to set the Content-Disposition header to attachment to get a 'Save As' dialogue. Here's a kickoff PHP example:

<?php
    header('Content-Type: application/pdf');
    header('Content-Disposition: attachment;filename="foo.pdf"');
    readfile('/path/to/foo.pdf');
?>

You can't and don't want to do this with Javascript.

Important note: due to a poor feature, in MSIE the default filename in 'Save As' dialogue won't be derived from the content-disposition header, it will instead be the last part of the pathinfo in the request URL. To workaround this, append the PDF filename to the link, e.g. http://example.com/pdf/foo.pdf. You can even make use of it in PHP to read the in the pathinfo specified PDF file. Here's a basic example of pdf.php:

<?php
    $file_name = $_SERVER['PATH_INFO'];
    $file = '/path/to/pdf/files' . $file_name;
    if (file_exists($file)) {
        header('Content-Type: application/pdf');
        header('Content-Disposition: attachment;filename="' . basename($file_name) . '"');
        header('Content-Length: ' . filesize($file));
        readfile($file);
    } else {
        header('HTTP/1.1 404 Not Found');
    }
?>

This however assumes that you've MultiViews on so that /pdf/ will go through this PHP file, or at least a RewriteRule from /pdf/ to /pdf.php/.

The major advantage of this approach is that you don't need to change the code whenever you want to add a new PDF file or change the PDF file name.

You can even make it more generic by automatically determining and setting the correct content type:

<?php
    $file_name = $_SERVER['PATH_INFO'];
    $file = '/path/to/all/files' . $file_name;
    if (file_exists($file)) {
        header('Content-Type: ' . mime_content_type($file_name));
        header('Content-Disposition: attachment;filename="' . basename($file_name) . '"');
        header('Content-Length: ' . filesize($file));
        readfile($file);
    } else {
        header('HTTP/1.1 404 Not Found');
    }
?>

Name it files.php or so and then you have a generic PHP downloader which you can access by for example http://example.com/files/foo.pdf, http://example.com/files/bar.zip, etcetera.

Hope this helps.

Upvotes: 13

rjmunro
rjmunro

Reputation: 28056

Rather than having to write PHP wrapper scripts, if you are using Apache, you can do this with a .htaccess file in the folder containing the PDFs:

<Files *.pdf>
  Header set Content-Disposition attachment
</Files>

Apparently some versions of IE / Adobe Reader don't respect the Content-Disposition header. You can work around these with ForceType application/octet-stream

<Files *.pdf>
  ForceType application/octet-stream
  Header set Content-Disposition attachment
</Files>

Upvotes: 3

Nirmal
Nirmal

Reputation: 9549

The following code may help you:

<?php
if(isset($_GET['docid'])){
    switch($_GET['docid']){
        case '1':
            $file = 'complete/path/to/pdf/file1';
            break;
        case '2':
            $file = 'complete/path/to/pdf/file2';
            break;
        case '3':
            $file = 'complete/path/to/pdf/file3';
            break;
        default:
            exit;
    }

    if(file_exists($file)){
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename='.basename($file));
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Pragma: public');
        header('Content-Length: ' . filesize($file));
        ob_clean();
        flush();
        readfile($file);
        exit;
    }
}

Save this code as a php file (say, download.php) and call it from the link. What the script does is that it reads the pdf file and outputs it to the buffer. The headers will force the disposition as download.

To call the first pdf, href to '/path/to/download.php?docid=1'
To call the second pdf, href to '/path/to/download.php?docid=2'
To call the third pdf, href to '/path/to/download.php?docid=3'

So, you don't need AJAX to do the work.

Upvotes: 1

Jeffrey Aylesworth
Jeffrey Aylesworth

Reputation: 8470

You can add an HTTP header to do that, there is an example in the PHP Docs (See example one)

Upvotes: 1

Amber
Amber

Reputation: 526523

Try using PHP to serve the file while first sending a header of Content-type: application/octet-stream.

Upvotes: 1

Related Questions