如何使HTML链接中的PDF文件可下载?


124

我在网页上提供pdf文件的链接以供下载,如下所示

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

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

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

但是,无论是否安装了“ Adob​​e acrobat”,我都希望它始终弹出用户下载。

请告诉我我该怎么做?

Answers:


115

而不是链接到.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:显然对“文件”变量进行了一些检查,以防止他人窃取您的文件,例如不接受文件扩展名,拒绝斜杠,在值中添加.pdf


2
我正面临另一个问题,我的文件位于/products/brochure/myfile.pdf中,我给$ file变量指定为$ file_path = $ _SERVER ['DOCUMENT_ROOT']。'/ products / brochure /'。$ file; 但其将文件下载为“%2Fvar%2Fwww%2Fweb15%2Fweb%2Fproducts%2Fbrochure%2myfile.pdf”
Prashant

3
@TravisO "Content-type: application/force-download"不在此处列出:iana.org/assignments/media-types/application这是一个完全虚假的标头。请不要编造标题并发送。您能否更新您的答案。谢谢。
Nicholas Wilson

4
但是,逐字使用此代码时要小心。由于您将GET变量直接传递到中,因此引入了一个严重的LFI漏洞fopen
Joost 2015年

4
此代码可能以另一种方式危险。如果您将HTTP链接传递给fopen,我认为它将从另一台服务器检索文件。攻击者可能会使用它来尝试扫描您的内部网络以查看暴露的PDF文件。
罗里·麦库恩

2
更不用说绕过您认为将对“ file”参数执行的任何“健全性检查”有多么容易。路径注入/目录遍历攻击极有可能发生。
AviD 2015年

209

这是一个常见问题,但很少有人知道有一个简单的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 caniuse.com/#feat=download
benebun 2013年

2
是的,我知道。这就是为什么我有关于兼容性的旁注。并且根据您的消息来源,IE和Safari均不支持这种方法,或者至少还不支持这种方法:)无论如何,如果您希望所有浏览器都强制下载,我建议改为检查其他一些答案...
T_D

3
与镀铬版本39.0.2171.65(64位)搭配使用时,就像魅力一样!
edelans 2014年

该解决方案很简单,但是很遗憾,IE和Safari不支持该解决方案。
valkirilov

说没有访问权限
Hackbal Teamz '18

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文件...


4
ReadFile的功能确实快。我个人建议使用此答案,而不是接受的答案
Jose Garrido

24

不用使用PHP脚本来读取和刷新文件,而是使用重写标题更为简洁.htaccess。这将保留一个“不错的” URL(myfile.pdf而不是download.php?myfile)。

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

这不会使您的所有PDF都强制下载吗?
TecBrat 2014年

1
@TecBrat是的,但是那是OP要求的。如果只想限制几个PDF,请编辑^\.pdf$正则表达式。
罗布W

1
@TecBrat或将.htaccess文件仅放在需要此行为的子文件夹中。
2015年

14

我找到了一种方法,可以用普通的旧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()该文件打开一个新窗口。


5

这是关键:

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

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


3

我知道我回答这个问题很晚了,但是我发现用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/">Download my eBook</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行,则可以用相同的方式进行设置,但我认为没有必要。


0

我使用PDF文件的整个网址(而不是仅将文件名或位置放入href)解决了我的问题:a href =“ domain。com / pdf / filename.pdf”


0

如果您需要限制下载速度,请使用此代码!

<?php
$local_file = 'file.zip';
$download_file = 'name.zip';

// set the download rate limit (=> 20,5 kb/s)
$download_rate = 20.5;
if(file_exists($local_file) && is_file($local_file))
{
header('Cache-control: private');
header('Content-Type: application/octet-stream');
header('Content-Length: '.filesize($local_file));
header('Content-Disposition: filename='.$download_file);

flush();
$file = fopen($local_file, "r");
while(!feof($file))
{
    // send the current file part to the browser
    print fread($file, round($download_rate * 1024));
    // flush the content to the browser
    flush();
    // sleep one second
    sleep(1);
}
fclose($file);}
else {
die('Error: The file '.$local_file.' does not exist!');
}

?>

欲了解更多信息,请点击这里


0
<!DOCTYPE html>  
<html xmlns="http://www.w3.org/1999/xhtml">  
<head>  
    <title>File Uploader</title>  
    <script src="../Script/angular1.3.8.js"></script>  
    <script src="../Script/angular-route.js"></script>  
    <script src="../UserScript/MyApp.js"></script>  
    <script src="../UserScript/FileUploder.js"></script>  
    <>  
        .percent {  
            position: absolute;  
            width: 300px;  
            height: 14px;  
            z-index: 1;  
            text-align: center;  
            font-size: 0.8em;  
            color: white;  
        }  

        .progress-bar {  
            width: 300px;  
            height: 14px;  
            border-radius: 10px;  
            border: 1px solid #CCC;  
            background-image: -webkit-gradient(linear, left top, left bottom, from(#6666cc), to(#4b4b95));  
            border-image: initial;  
        }  

        .uploaded {  
            padding: 0;  
            height: 14px;  
            border-radius: 10px;  
            background-image: -webkit-gradient(linear, left top, left bottom, from(#66cc00), to(#4b9500));  
            border-image: initial;  
        }  
    </>  
</head>  
<body ng-app="MyApp" ng-controller="FileUploder">  
    <div>  
        <table ="width:100%;border:solid;">  
            <tr>  
                <td>Select File</td>  
                <td>  
                    <input type="file" ng-model-instant id="fileToUpload" onchange="angular.element(this).scope().setFiles(this)" />  
                </td>  
            </tr>  
            <tr>  
                <td>File Size</td>  
                <td>  
                    <div ng-repeat="file in files.slice(0)">  
                        <span ng-switch="file.size > 1024*1024">  
                            <span ng-switch-when="true">{{file.size / 1024 / 1024 | number:2}} MB</span>  
                            <span ng-switch-default>{{file.size / 1024 | number:2}} kB</span>  
                        </span>  
                    </div>  
                </td>  
            </tr>             
            <tr>  
                <td>  
                    File Attach Status  
                </td>  
                <td>{{AttachStatus}}</td>  
            </tr>  
            <tr>  
                <td>  
                    <input type="button" value="Upload" ng-click="fnUpload();" />  
                </td>  
                <td>  
                    <input type="button" value="DownLoad" ng-click="fnDownLoad();" />  
                </td>  
            </tr>  
        </table>  
    </div>  
</body>  
</html>   

1
您应该解释代码中提供的内容。即使不是详细内容或不需要详细内容,也可以是简短形式。
Suraj Kumar

-1

在Ruby on Rails应用程序中(尤其是使用Prawn gem和Prawnto Rails插件之类的东西),您可以比使用完整脚本(如先前的PHP示例)更简单地完成此操作。

在您的控制器中:

def index

 respond_to do |format|
   format.html # Your HTML view
   format.pdf { render :layout => false }
 end
end

render:layout => false部分告诉浏览器打开“您要下载此文件吗?” 提示而不是尝试呈现PDF。然后,您将能够正常链接到该文件:http : //mysite.com/myawesomepdf.pdf


哪里写这段代码?哪个控制器,我是PHP新手,请解释。
Prashant

@Prashant这不是PHP,而是Ruby on Rails。
eloyesp

@Prashant:如果您需要一个PHP示例,请查看线程中更远的示例,但是请阅读有关它们可能不安全的注释。
Evan Donovan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.