PDFファイルをHTMLリンクでダウンロード可能にするにはどうすればよいですか?


124

以下のように、ダウンロード用に私のWebページにPDFファイルのリンクを提供しています

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

問題は、ユーザーがこのリンクをクリックすると

  • ユーザーがAdobe Acrobatをインストールしている場合は、Adobe Readerの同じブラウザーウィンドウでファイルを開きます。
  • Adobe Acrobatがインストールされていない場合は、ファイルをダウンロードするためのポップアップが表示されます。

ただし、「Adobe acrobat」がインストールされているかどうかに関係なく、ユーザーに常にポップアップ表示してダウンロードしてほしい。

どうすればこれを行うことができますか?

回答:


116

.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%2myfile.pdf」としてファイルをダウンロード
Prashant

3
@TravisO "Content-type: application/force-download"はここのどこにもリストされていません:iana.org/assignments/media-types/applicationこれは完全に偽のヘッダーです。ヘッダーを作成して送信しないでください。回答を更新してください。ありがとう。
ニコラスウィルソン

4
ただし、このコードをそのまま使用する場合は注意してください。GET変数をに直接渡すため、これは重大なLFIの脆弱性をもたらしますfopen
Joost、2015

4
このコードはおそらく別の方法で危険です。HTTPリンクをfopenに渡すと、別のサーバーからファイルを取得できると思います。攻撃者が内部ネットワークをスキャンして、公開されたPDFファイルをスキャンしようとする可能性があります。
Rory McCune

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

2
うん、そうだね。そのため、互換性についての補足説明があります。そして、ソースによると、IEとSafariの両方がこのアプローチをサポートしていないか、少なくともまだサポートしていません:)とにかく、すべてのブラウザーに強制的にダウンロードさせたい場合は、代わりに他の回答のいくつかを確認することをお勧めします...
T_D

3
クロムバージョン39.0.2171.65(64ビット)でチャームのように動作します!
edelans 2014年

解決策は簡単ですが、残念ながらIEとSafariではサポートされていません。
valkirilov 2016

へのアクセス許可がないと述べている
Hackbal Teamz 2018

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;
}

誰かがいくつかのphpファイルをダウンロードする可能性があるので、get変数をサニタイズしてください...


4
ReadFile関数は確かに高速です。私は個人的に、受け入れられた回答の代わりにこの回答を使用することをお勧めします
ホセガリド

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$正規表現を編集します。
Rob 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");

Content-type application / x-pdf-documentまたはapplication / pdfが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へのアクセス権があれば、これもhttpd.confに入れることができます。

<FilesMatch "\.(?i:pdf)$">
  Header set Content-Disposition attachment
</FilesMatch>

FilesMatchディレクティブは単なる正規表現であるため、必要に応じて細かく設定したり、他の拡張子を追加したりできます。

ヘッダー行は、上記のPHPスクリプトの最初の行と同じことを行います。Content-Type行も設定する必要がある場合は、同じ方法で設定できますが、必要なことはわかりません。


0

私はPDFファイルの完全なURLを使用して解決しました(ファイル名または場所を単に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 2013

@Prashant:PHPの例が必要な場合は、スレッドの上の例を見てください。ただし、それらが安全でない可能性があることについてのコメントを読んでください。
Evan Donovan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.