How to make PDF file downloadable in HTML link?

711,130

Solution 1

Instead of linking to the .PDF file, instead do something like

<a href="pdf_server.php?file=pdffilename">Download my eBook</a>

which outputs a custom header, opens the PDF (binary safe) and prints the data to the user's browser, then they can choose to save the PDF despite their browser settings. The pdf_server.php should look like this:

header("Content-Type: application/octet-stream");

$file = $_GET["file"] .".pdf";
header("Content-Disposition: attachment; filename=" . urlencode($file));   
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Description: File Transfer");            
header("Content-Length: " . filesize($file));
flush(); // this doesn't really matter.
$fp = fopen($file, "r");
while (!feof($fp))
{
    echo fread($fp, 65536);
    flush(); // this is essential for large downloads
} 
fclose($fp); 

PS: and obviously run some sanity checks on the "file" variable to prevent people from stealing your files such as don't accept file extensions, deny slashes, add .pdf to the value

Solution 2

This is a common issue but few people know there's a simple HTML 5 solution:

<a href="./directory/yourfile.pdf" download="newfilename">Download the pdf</a>

Where newfilename is the suggested filename for the user to save the file. Or it will default to the filename on the serverside if you leave it empty, like this:

<a href="./directory/yourfile.pdf" download>Download the pdf</a>

Compatibility: I tested this on Firefox 21 and Iron, both worked fine. It might not work on HTML5-incompatible or outdated browsers. The only browser I tested that didn't force download is IE...

Check compatibility here: http://caniuse.com/#feat=download

Solution 3

Don't loop through every file line. Use readfile instead, its faster. This is off the php site: http://php.net/manual/en/function.readfile.php

$file = $_GET["file"];
if (file_exists($file)) {
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header("Content-Type: application/force-download");
    header('Content-Disposition: attachment; filename=' . urlencode(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;
}

Make sure to sanitize your get variable as someone could download some php files...

Solution 4

Instead of using a PHP script, to read and flush the file, it's more neat to rewrite the header using .htaccess. This will keep a "nice" URL (myfile.pdf instead of download.php?myfile).

<FilesMatch "\.pdf$">
ForceType applicaton/octet-stream
Header set Content-Disposition attachment
</FilesMatch>

Solution 5

I found a way to do it with plain old HTML and JavaScript/jQuery that degrades gracefully. Tested in IE7-10, Safari, Chrome, and FF:

HTML for download link:

<p>Thanks for downloading! If your download doesn't start shortly, 
<a id="downloadLink" href="...yourpdf.pdf" target="_blank" 
type="application/octet-stream" download="yourpdf.pdf">click here</a>.</p>

jQuery (pure JavaScript code would be more verbose) that simulates clicking on link after a small delay:

var delay = 3000;
window.setTimeout(function(){$('#downloadLink')[0].click();},delay);

To make this more robust you could add HTML5 feature detection and if it's not there then use window.open() to open a new window with the file.

Share:
711,130
djmzfKnm
Author by

djmzfKnm

WebDev

Updated on November 20, 2020

Comments

  • djmzfKnm
    djmzfKnm over 3 years

    I am giving link of a pdf file on my web page for download, like below

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

    The problem is when user clicks on this link then

    • If the user have installed Adobe Acrobat, then it opens the file in the same browser window in Adobe Reader.
    • If the Adobe Acrobat is not installed then it pop-up to the user for Downloading the file.

    But I want it always pop-up to the user for download, irrespective of "Adobe acrobat" is installed or not.

    Please tell me how i can do this?

  • djmzfKnm
    djmzfKnm over 15 years
    Is it really matter if i use the following code $file = $_GET["file"] .".pdf"; AS $file = $_REQUEST["file"] .".pdf";
  • djmzfKnm
    djmzfKnm over 15 years
    Where to wrote this code? which controller, i am new to PHP please explain.
  • djmzfKnm
    djmzfKnm over 15 years
    I am facing another problem with this, that my file is located at /products/brochure/myfile.pdf I am giving $file variable as $file_path = $_SERVER['DOCUMENT_ROOT'].'/products/brochure/' . $file; but its downloading the file as "%2Fvar%2Fwww%2Fweb15%2Fweb%2Fproducts%2Fbrochure%2myfile.pd‌​f"
  • djmzfKnm
    djmzfKnm over 15 years
    what i can do to make it downloaded with the name "myfile.pdf" only. I changed the header("Content-Disposition: attachment; filename=" . urlencode('myfile.pdf')); also, but still its not working.
  • Jose Garrido
    Jose Garrido about 11 years
    The readfile function is indeed faster. I personally recommend using this answer instead of the accepted one
  • eloyesp
    eloyesp about 11 years
    @Prashant This is not PHP, it's Ruby on Rails.
  • Tommz
    Tommz about 11 years
    Am I the only one getting error that .pdf was damaged when I'm trying to open .pdf?
  • Nicholas Wilson
    Nicholas Wilson over 10 years
    @TravisO "Content-type: application/force-download" isn't listed anywhere here: iana.org/assignments/media-types/application It's a completely bogus header. Please don't make up headers and send them. Could you update your answer. Thanks.
  • TravisO
    TravisO over 10 years
    After Googling about it and double checking, it's removed
  • benebun
    benebun over 10 years
    This is a simple solution but unfortunately not very widely supported, esp. no support by IE caniuse.com/#feat=download
  • T_D
    T_D over 10 years
    Yep, I know right. That's why I have the side-note on compatibility. And according to your source both IE and Safari don't support this approach, or at least not yet :) Anyhow, if you want all browsers to force download I suggest checking some of the other answers instead...
  • TecBrat
    TecBrat over 9 years
    Wouldn't this make ALL your PDFs force download?
  • Rob W
    Rob W over 9 years
    @TecBrat Yes, but that was what the OP asked. If you want to limit to a few PDFs only, edit the ^\.pdf$ regular expression.
  • Edwin Thomas
    Edwin Thomas over 9 years
    @TravisO I Got an Error, it is either not a supported file type or because the file has been damaged...
  • edelans
    edelans over 9 years
    works like a charm with chrome Version 39.0.2171.65 (64-bit) !
  • Joost
    Joost about 9 years
    Be careful when using this code verbatim, though. This introduces a serious LFI vulnerability, as you're passing GET-variables directly into fopen.
  • habakuk
    habakuk about 9 years
    @TecBrat or put the .htaccess file only in the subfolder where this behavior is needed.
  • Rory McCune
    Rory McCune over 8 years
    This code is likely dangerous in another way. If you pass HTTP links to fopen, I think it'll go retrieve the file from another server. Could be used by an attacker to attempt to scan your internal network for exposed PDF files.
  • AviD
    AviD over 8 years
    Not to mention how easy it would be to bypass any "sanity checks" you think you'll be doing to the "file" parameter. Path injection / directory traversal attacks are extremely likely.
  • valkirilov
    valkirilov almost 8 years
    The solution is easy but unfortunately not supported in IE and Safari.
  • Evan Donovan
    Evan Donovan over 7 years
    If I were going to use this code, instead of doing sanity checks, I would have a database of whitelisted files on my server that could be used with it. That's what various CMS do. As for @RоryMcCune's comment, I think it depends on how your PHP is set up. Imo, php.ini should be configured to prevent fopen from opening things from remote servers.
  • Evan Donovan
    Evan Donovan over 7 years
    @Prashant: If you need a PHP example, look at the ones further up the thread, but please read the comments about how they could be insecure.
  • Evan Donovan
    Evan Donovan over 7 years
    You could also add to your code something that would check for an additional $_GET parameter that would act as a "secret key" - that way people couldn't craft arbitrary URLs since they wouldn't know the key you were using to permit the file to be served. It wouldn't be perfectly secure but it would be an improvement.
  • Abdulla Sirajudeen
    Abdulla Sirajudeen over 5 years
    says no permission to access
  • Suraj Kumar
    Suraj Kumar about 5 years
    You should explain what you have provided in your code. Even in short form if not possible in details or not required in details.
  • divy3993
    divy3993 almost 4 years
    I think this answer is not valid today, when we have better alternative of HTML5 attribute download
  • Aljohn Yamaro
    Aljohn Yamaro almost 3 years
    Sorry, not working, it will replace the current URL of the browser to pdf url
  • jamesdlin
    jamesdlin about 2 years
    Even for browsers where it's supported, whether or not the download attribute triggers a download prompt depends on the user's browser settings. For example, even with Firefox 98, clicking a link to a .pdf file by default opens it in the browser, regardless of the download attribute. Other than being able to suggest a filename, the download attribute doesn't seem to offer much benefit.