如何在HTML链接中实现PDF文件下载?

149

我在网页上提供了一个PDF文件的下载链接,如下所示:

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

问题是当用户单击此链接时,

  • 如果用户已安装Adobe Acrobat,则在Adobe Reader中的同一浏览器窗口中打开文件。
  • 如果未安装Adobe Acrobat,则弹出下载文件的提示框。

但我希望它始终弹出下载提示框,无论"Adobe Acrobat"是否安装。

请告诉我如何实现这个功能?

14个回答

260

这是一个常见的问题,但很少有人知道有一个简单的HTML 5解决方案:

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

其中newfilename是建议用户保存该文件时使用的文件名。如果您将其留空,它将使用服务器端上的文件名作为默认文件名:

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

兼容性:我已在Firefox 21和Iron上测试过,均正常工作。它可能不适用于不支持HTML5或过时的浏览器。我测试的唯一一个未强制下载的浏览器是IE...

在这里检查兼容性:http://caniuse.com/#feat=download


9
这是一个简单的解决方案,但不幸的是它得到的支持并不广泛,特别是IE浏览器不支持。http://caniuse.com/#feat=download - benebun
2
是的,我知道。这就是为什么我在兼容性方面有一个侧注。根据你的来源,IE和Safari都不支持这种方法,或者至少还没有支持 :) 无论如何,如果你想让所有浏览器都强制下载,我建议你查看其他答案... - T_D
3
完美地在 Chrome 版本 39.0.2171.65 (64 位) 上运行! - edelans
解决方案很简单,但不幸的是在IE和Safari中不受支持。 - valkirilov
没有权限访问。 - Abdulla Sirajudeen
即使在支持的浏览器中,download属性是否触发下载提示取决于用户的浏览器设置。例如,即使在Firefox 98中,单击链接到.pdf文件默认情况下会在浏览器中打开它,而不考虑download属性。除了能够建议文件名之外,download属性似乎没有提供太多好处。 - jamesdlin

121

不要链接至 PDF 文件,而是尝试使用以下方式

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

这段代码会输出一个自定义的头部,打开PDF(二进制安全),并将数据打印到用户的浏览器上,然后用户可以选择保存PDF文件,即使他们的浏览器设置不允许。pdf_server.php应该长这样:

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:显然需要对“file”变量进行一些合理性检查,以防止人们窃取您的文件,例如不接受文件扩展名、拒绝斜杠,并将.pdf添加到值中。


2
我在处理另一个问题,我的文件位于/products/brochure/myfile.pdf。我将$file变量指定为$file_path = $_SERVER['DOCUMENT_ROOT'].'/products/brochure/' . $file; 但它下载的文件名是 "%2Fvar%2Fwww%2Fweb15%2Fweb%2Fproducts%2Fbrochure%2Fmyfile.pdf"。 - djmzfKnm
3
“Content-type: application/force-download”在http://www.iana.org/assignments/media-types/application中没有列出。这是一个完全虚假的标题。请不要编造标题并发送它们。您能否更新您的答案?谢谢。 - Nicholas Wilson
4
直接使用这段代码时要小心,因为你会将GET变量直接传递给 fopen,从而引入严重的LFI漏洞。 - Joost
4
这段代码存在另一种潜在的危险。如果你向fopen传递HTTP链接,它可能会从另一个服务器检索文件。这可能被攻击者用来扫描内部网络中暴露的PDF文件。 - Rory McCune
2
更不用说绕过你认为会对“文件”参数进行的任何“合法性检查”有多容易了。路径注入/目录遍历攻击非常可能发生。 - AviD
显示剩余8条评论

50

不要使用循环遍历每一行文件。 使用readfile函数代替,它更快。这是从php网站上获取的信息: 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;
}

一定要对您的get变量进行清理,因为有人可能会下载一些php文件...


5
"readfile" 函数确实更快。我个人建议使用这个答案,而不是接受的那个。 - Jose Garrido

24

使用.htaccess重写头部信息比使用PHP脚本来读取和刷新文件更简洁。这样可以使URL更美观(例如:myfile.pdf而不是download.php?myfile)。

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

这会不会让你的所有PDF都强制下载呢? - TecBrat
1
@TecBrat 是的,但这是OP所问的。如果您只想限制为几个PDF文件,编辑“^ \ .pdf $”正则表达式即可。 - Rob W
2
@TecBrat 或者只将 .htaccess 文件放在需要此行为的子文件夹中。 - habakuk

15
我发现了一种使用纯HTML和JavaScript/jQuery的方法来实现它,能够平稳退化。在IE7-10、Safari、Chrome和FF中测试过:
下载链接的HTML代码:
<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(纯JavaScript代码会更冗长)模拟在小延迟后点击链接:
var delay = 3000;
window.setTimeout(function(){$('#downloadLink')[0].click();},delay);

为了使其更加健壮,您可以添加HTML5功能检测。如果不存在,则使用window.open()打开一个新窗口并显示文件。

8
这是钥匙:
header("Content-Type: application/octet-stream");

发送PDF文件时,通常会发送Content-type应用程序/ x-pdf-document或应用程序/ pdf。Adobe Reader通常为此MIME类型设置处理程序,因此当接收到任何PDF MIME类型的文档时,浏览器将将文档传递给Adobe Reader。

5
可以使用HTML来实现此功能。
<a href="myfile.pdf">Download Brochure</a>

将下载属性添加到其中: 这里的文件名将是myfile.pdf
<a href="myfile.pdf" download>Download Brochure</a>

为下载属性指定一个值:

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

这里的文件名将是Brochure.pdf。


抱歉,无法工作,它将替换浏览器当前的URL为PDF URL。 - Aljohn Yamaro
不工作,顺便说一下:我的PDF链接是一个AWS S3链接。 - Farhan Syed

2

我知道回答已经很晚了,但我发现了一种用JavaScript实现的技巧。

function downloadFile(src){
    var link=document.createElement('a');
    document.body.appendChild(link);
    link.href= src;
    link.download = '';
    link.click();
}

0

尝试这个:

<a href="pdf_server_with_path.php?file=pdffilename&path=http://myurl.com/mypath/">下载我的电子书</a>

pdf_server_with_path.php 中的代码是:

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

$file = $_GET["file"] .".pdf";
$path = $_GET["path"];
$fullfile = $path.$file;

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

0
这里有一种不同的方法。我更喜欢使用Web服务器逻辑,而不是依赖浏览器支持或在应用程序层面解决这个问题。
如果您正在使用Apache,并且可以将.htaccess文件放置在相关目录中,则可以使用下面的代码。当然,如果您可以访问httpd.conf,也可以将其放置在其中。
<FilesMatch "\.(?i:pdf)$">
  Header set Content-Disposition attachment
</FilesMatch>

FilesMatch指令只是一个正则表达式,因此可以设置得非常细致,或者您可以添加其他扩展名。

Header行与上面的PHP脚本中的第一行执行相同的操作。如果您还需要设置Content-Type行,可以以相同的方式进行设置,但我发现这并不必要。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接