Wie kann ich eine PDF-Datei in einem HTML-Link herunterladen?

124

Ich gebe den Link einer PDF-Datei auf meiner Webseite zum Download, wie unten

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

Das Problem ist, wenn der Benutzer dann auf diesen Link klickt

  • Wenn der Benutzer Adobe Acrobat installiert hat, wird die Datei im selben Browserfenster in Adobe Reader geöffnet.
  • Wenn Adobe Acrobat nicht installiert ist, wird es dem Benutzer zum Herunterladen der Datei angezeigt.

Ich möchte jedoch, dass es dem Benutzer immer zum Herunterladen angezeigt wird, unabhängig davon, ob "Adobe Acrobat" installiert ist oder nicht.

Bitte sag mir, wie ich das machen kann.

Prashant
quelle

Antworten:

115

Anstatt eine Verknüpfung mit der PDF-Datei herzustellen, führen Sie stattdessen Folgendes aus

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

Dieser gibt einen benutzerdefinierten Header aus, öffnet die PDF-Datei (Binärtresor) und druckt die Daten im Browser des Benutzers aus. Anschließend können sie die PDF-Datei trotz ihrer Browsereinstellungen speichern. Die Datei pdf_server.php sollte folgendermaßen aussehen:

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: Führen Sie offensichtlich einige Sicherheitsüberprüfungen für die Variable "Datei" durch, um zu verhindern, dass Personen Ihre Dateien stehlen, z. B. Dateierweiterungen nicht akzeptieren, Schrägstriche verweigern und dem Wert PDF hinzufügen

TravisO
quelle
2
Ich habe ein weiteres Problem damit, dass sich meine Datei unter /products/brochure/myfile.pdf befindet. Ich gebe $ file variable als $ file_path = $ _SERVER ['DOCUMENT_ROOT']. '/ Products / brochure /'. $ file; aber es lädt die Datei als "% 2Fvar% 2Fwww% 2Fweb15% 2Fweb% 2Fproducts% 2Fbrochure% 2myfile.pdf" herunter
Prashant
3
@TravisO "Content-type: application/force-download"ist hier nirgendwo aufgeführt: iana.org/assignments/media-types/application Es ist ein völlig gefälschter Header. Bitte machen Sie keine Header und senden Sie sie. Könnten Sie Ihre Antwort aktualisieren? Vielen Dank.
Nicholas Wilson
4
Seien Sie jedoch vorsichtig, wenn Sie diesen Code wörtlich verwenden. Dies führt zu einer schwerwiegenden LFI-Sicherheitsanfälligkeit, da Sie GET-Variablen direkt übergeben fopen.
Joost
4
Dieser Code ist wahrscheinlich auf andere Weise gefährlich. Wenn Sie HTTP-Links an fopen übergeben, wird die Datei wahrscheinlich von einem anderen Server abgerufen. Kann von einem Angreifer verwendet werden, um zu versuchen, Ihr internes Netzwerk nach exponierten PDF-Dateien zu durchsuchen.
Rory McCune
2
Ganz zu schweigen davon, wie einfach es wäre, alle "Sanity Checks" zu umgehen, von denen Sie glauben, dass Sie sie mit dem Parameter "file" durchführen werden. Pfadinjektions- / Verzeichnisüberquerungsangriffe sind äußerst wahrscheinlich.
AviD
209

Dies ist ein häufiges Problem, aber nur wenige wissen, dass es eine einfache HTML 5-Lösung gibt:

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

Wo newfilenameist der vorgeschlagene Dateiname, unter dem der Benutzer die Datei speichern kann? Oder es wird standardmäßig der Dateiname auf der Serverseite verwendet, wenn Sie ihn leer lassen, wie folgt:

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

Kompatibilität: Ich habe dies auf Firefox 21 und Iron getestet, beide haben gut funktioniert. Es funktioniert möglicherweise nicht mit HTML5-inkompatiblen oder veralteten Browsern. Der einzige Browser, den ich getestet habe und der den Download nicht erzwungen hat, ist IE ...

Überprüfen Sie die Kompatibilität hier: http://caniuse.com/#feat=download

T_D
quelle
9
Dies ist eine einfache Lösung, die aber leider nicht sehr weit verbreitet ist. Keine Unterstützung durch IE caniuse.com/#feat=download
benebun
2
Ja, ich weiß es richtig. Deshalb habe ich die Randnotiz zur Kompatibilität. Und laut Ihrer Quelle unterstützen sowohl IE als auch Safari diesen Ansatz nicht oder zumindest noch nicht :) Wenn Sie möchten, dass alle Browser den Download erzwingen, schlage ich vor, stattdessen einige der anderen Antworten zu überprüfen ...
T_D
3
funktioniert wie ein Zauber mit Chrome Version 39.0.2171.65 (64-Bit)!
Edelans
Die Lösung ist einfach, wird aber in IE und Safari leider nicht unterstützt.
Valkirilov
sagt keine Erlaubnis zum Zugriff
Hackbal Teamz
50

Durchlaufen Sie nicht jede Dateizeile. Verwenden Sie stattdessen Readfile , es ist schneller. Dies ist von der 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;
}

Stellen Sie sicher, dass Sie Ihre get-Variable bereinigen, da jemand einige PHP-Dateien herunterladen könnte ...

Alex V.
quelle
4
Die Readfile- Funktion ist in der Tat schneller. Ich persönlich empfehle, diese Antwort anstelle der akzeptierten zu verwenden
Jose Garrido
24

Anstatt ein PHP-Skript zum Lesen und Leeren der Datei zu verwenden, ist es einfacher, den Header mit neu zu schreiben .htaccess. Dadurch bleibt eine "nette" URL erhalten ( myfile.pdfanstelle von download.php?myfile).

<FilesMatch "\.pdf$">
ForceType applicaton/octet-stream
Header set Content-Disposition attachment
</FilesMatch>
Rob W.
quelle
Würde dies nicht dazu führen, dass ALLE Ihre PDFs den Download erzwingen?
TecBrat
1
@TecBrat Ja, aber das hat das OP gefragt. Wenn Sie sich nur auf wenige PDF-Dateien beschränken möchten, bearbeiten Sie den ^\.pdf$regulären Ausdruck.
Rob W
1
@TecBrat oder legen Sie die .htaccess-Datei nur in dem Unterordner ab, in dem dieses Verhalten benötigt wird.
Habakuk
14

Ich habe einen Weg gefunden, dies mit einfachem altem HTML und JavaScript / jQuery zu tun, der sich elegant verschlechtert. Getestet in IE7-10, Safari, Chrome und FF:

HTML zum 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 (reiner JavaScript-Code wäre ausführlicher), der das Klicken auf einen Link nach einer kleinen Verzögerung simuliert:

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

Um dies robuster zu machen, können Sie die HTML5-Feature-Erkennung hinzufügen. Wenn diese nicht vorhanden ist window.open(), können Sie ein neues Fenster mit der Datei öffnen.

Alex W.
quelle
5

Das ist der Schlüssel:

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

Beim Senden einer PDF-Datei wird eine inhaltsbezogene Anwendung / x-pdf-document oder application / pdf gesendet. Adobe Reader legt normalerweise den Handler für diesen MIME-Typ fest, sodass der Browser das Dokument an Adobe Reader weiterleitet, wenn einer der PDF-MIME-Typen empfangen wird.

Plötzliche Def
quelle
3

Ich weiß, dass ich sehr spät bin, um darauf zu antworten, aber ich habe einen Hack gefunden, um dies in Javascript zu tun.

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

Versuche dies:

<a href="pdf_server_with_path.php?file=pdffilename&path=http://myurl.com/mypath/">Download my eBook</a>

Der Code in pdf_server_with_path.php lautet:

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);
Saill
quelle
0

Hier ist ein anderer Ansatz. Ich bevorzuge es, mich auf die Browserunterstützung zu verlassen oder dies auf der Anwendungsebene anzusprechen, um die Webserverlogik zu verwenden.

Wenn Sie Apache verwenden und eine .htaccess-Datei in das entsprechende Verzeichnis stellen können, können Sie den folgenden Code verwenden. Natürlich können Sie dies auch in httpd.conf einfügen, wenn Sie Zugriff darauf haben.

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

Die FilesMatch-Direktive ist nur ein regulärer Ausdruck, sodass sie beliebig detailliert festgelegt oder in anderen Erweiterungen hinzugefügt werden kann.

Die Header-Zeile macht dasselbe wie die erste Zeile in den obigen PHP-Skripten. Wenn Sie auch die Zeilen für den Inhaltstyp festlegen müssen, können Sie dies auf die gleiche Weise tun, aber ich habe dies nicht für erforderlich befunden.

Evan Donovan
quelle
0

Ich habe meine mit der gesamten URL der PDF-Datei gelöst (anstatt nur den Dateinamen oder den Speicherort in href zu setzen): a href = "domain. Com / pdf / filename.pdf"

Mark Allena
quelle
0

Wenn Sie die Download-Rate begrenzen müssen, verwenden Sie diesen Code!

<?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!');
}

?>

Für weitere Informationen klicken Sie hier

Mehran Hooshangi
quelle
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>   
user11021789
quelle
1
Sie sollten erklären, was Sie in Ihrem Code angegeben haben. Auch in Kurzform, wenn im Detail nicht möglich oder im Detail nicht erforderlich.
Suraj Kumar
-1

In einer Ruby on Rails-Anwendung (insbesondere mit etwas wie dem Prawn Gem und dem Prawnto Rails-Plugin) können Sie dies etwas einfacher als mit einem vollständigen On-Skript (wie im vorherigen PHP-Beispiel) erreichen.

In Ihrem Controller:

def index

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

Der Teil render: layout => false weist den Browser an, das Feld "Möchten Sie diese Datei herunterladen?" Zu öffnen. Eingabeaufforderung, anstatt zu versuchen, das PDF zu rendern. Dann können Sie normalerweise auf die Datei verlinken: http://mysite.com/myawesomepdf.pdf

Übrigens
quelle
Wo soll ich diesen Code schreiben? Welchen Controller, ich bin neu in PHP, bitte erklären.
Prashant
@Prashant Dies ist kein PHP, sondern Ruby on Rails.
Eloyesp
@Prashant: Wenn Sie ein PHP-Beispiel benötigen, schauen Sie sich die weiter oben im Thread an, aber lesen Sie bitte die Kommentare darüber, wie sie unsicher sein könnten.
Evan Donovan