Comment rendre le fichier PDF téléchargeable en lien HTML?

124

Je donne le lien d'un fichier pdf sur ma page Web à télécharger, comme ci-dessous

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

Le problème est lorsque l'utilisateur clique sur ce lien puis

  • Si l'utilisateur a installé Adobe Acrobat, il ouvre le fichier dans la même fenêtre de navigateur dans Adobe Reader.
  • Si Adobe Acrobat n'est pas installé, il apparaîtra à l'utilisateur pour le téléchargement du fichier.

Mais je veux qu'il soit toujours pop-up à l'utilisateur pour le téléchargement, indépendamment de "Adobe acrobat" est installé ou non.

S'il vous plaît dites-moi comment je peux faire cela?

Prashant
la source

Réponses:

116

Au lieu de créer un lien vers le fichier .PDF, faites plutôt quelque chose comme

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

qui génère un en-tête personnalisé, ouvre le PDF (coffre-fort binaire) et imprime les données dans le navigateur de l'utilisateur, puis ils peuvent choisir d'enregistrer le PDF malgré les paramètres de leur navigateur. Le fichier pdf_server.php devrait ressembler à ceci:

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: et exécutez évidemment des vérifications de cohérence sur la variable "fichier" pour empêcher les gens de voler vos fichiers comme ne pas accepter les extensions de fichier, refuser les barres obliques, ajouter .pdf à la valeur

TravisO
la source
2
Je suis confronté à un autre problème avec ceci, que mon fichier se trouve dans /products/brochure/myfile.pdf Je donne la variable $ file comme $ file_path = $ _SERVER ['DOCUMENT_ROOT']. '/ Products / brochure /'. $ fichier; mais il télécharge le fichier en tant que "% 2Fvar% 2Fwww% 2Fweb15% 2Fweb% 2Fproducts% 2Fbrochure% 2myfile.pdf"
Prashant
3
@TravisO "Content-type: application/force-download"n'est répertorié nulle part ici: iana.org/assignments/media-types/application C'est un en-tête complètement faux. Veuillez ne pas créer d'en-têtes et les envoyer. Pourriez-vous mettre à jour votre réponse. Merci.
Nicholas Wilson
4
Soyez prudent lorsque vous utilisez ce code textuellement. Cela introduit une grave vulnérabilité LFI, car vous passez directement des variables GET dans fopen.
Joost
4
Ce code est probablement dangereux d'une autre manière. Si vous passez des liens HTTP à fopen, je pense qu'il récupérera le fichier d'un autre serveur. Peut être utilisé par un attaquant pour tenter de scanner votre réseau interne à la recherche de fichiers PDF exposés.
Rory McCune le
2
Sans parler de la facilité avec laquelle il serait facile de contourner les "vérifications de cohérence" que vous pensez faire pour le paramètre "file". Les attaques par injection de chemin / traversée de répertoire sont extrêmement probables.
AviD le
210

C'est un problème courant, mais peu de gens savent qu'il existe une solution HTML 5 simple:

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

newfilenameest le nom de fichier suggéré à l'utilisateur pour enregistrer le fichier. Ou il sera par défaut le nom de fichier sur le serveur si vous le laissez vide, comme ceci:

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

Compatibilité: j'ai testé cela sur Firefox 21 et Iron, les deux fonctionnaient bien. Cela peut ne pas fonctionner sur les navigateurs compatibles avec HTML5 ou obsolètes. Le seul navigateur que j'ai testé qui n'a pas forcé le téléchargement est IE ...

Vérifiez la compatibilité ici: http://caniuse.com/#feat=download

T_D
la source
9
C'est une solution simple mais malheureusement pas très largement supportée, en particulier. pas de support par IE caniuse.com/#feat=download
benebun
2
Oui, je sais bien. C'est pourquoi j'ai la note d'accompagnement sur la compatibilité. Et selon votre source, IE et Safari ne prennent pas en charge cette approche, ou du moins pas encore :) De toute façon, si vous voulez que tous les navigateurs forcent le téléchargement, je suggère de vérifier certaines des autres réponses à la place ...
T_D
3
fonctionne comme un charme avec Chrome Version 39.0.2171.65 (64 bits)!
edelans
La solution est simple mais malheureusement non prise en charge dans IE et Safari.
valkirilov le
dit aucune autorisation d'accès
Hackbal Teamz
50

Ne parcourez pas toutes les lignes de fichier. Utilisez plutôt readfile , c'est plus rapide. Ceci est en dehors du site 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;
}

Assurez-vous de nettoyer votre variable get car quelqu'un pourrait télécharger des fichiers php ...

Alex V
la source
4
La fonction readfile est en effet plus rapide. Je recommande personnellement d'utiliser cette réponse au lieu de celle acceptée
Jose Garrido
24

Au lieu d'utiliser un script PHP, pour lire et vider le fichier, il est plus judicieux de réécrire l'en-tête en utilisant .htaccess. Cela gardera une "belle" URL ( myfile.pdfau lieu de download.php?myfile).

<FilesMatch "\.pdf$">
ForceType applicaton/octet-stream
Header set Content-Disposition attachment
</FilesMatch>
Rob W
la source
Cela ne forcerait-il pas TOUS vos PDF à être téléchargés?
TecBrat
1
@TecBrat Oui, mais c'est ce que le PO a demandé. Si vous souhaitez limiter à quelques fichiers PDF uniquement, modifiez l' ^\.pdf$expression régulière.
Rob W
1
@TecBrat ou placez le fichier .htaccess uniquement dans le sous-dossier où ce comportement est nécessaire.
habakuk
14

J'ai trouvé un moyen de le faire avec du vieux HTML et JavaScript / jQuery qui se dégradent gracieusement. Testé dans IE7-10, Safari, Chrome et FF:

HTML pour le lien de téléchargement:

<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 (du code JavaScript pur serait plus détaillé) qui simule un clic sur le lien après un petit délai:

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

Pour rendre cela plus robuste, vous pouvez ajouter la détection des fonctionnalités HTML5 et si ce n'est pas là, utilisez window.open()pour ouvrir une nouvelle fenêtre avec le fichier.

Alex W
la source
5

Voici la clé:

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

Une application de type contenu / document x-pdf ou application / pdf est envoyée lors de l'envoi du fichier PDF. Adobe Reader définit généralement le gestionnaire pour ce type MIME afin que le navigateur transmette le document à Adobe Reader lorsque l'un des types PDF MIME est reçu.

Déf soudain
la source
3

Je sais que je suis très en retard pour répondre à cela mais j'ai trouvé un hack pour le faire en javascript.

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

Essaye ça:

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

Le code dans pdf_server_with_path.php est:

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
la source
0

Voici une approche différente. Je préfère plutôt que de compter sur la prise en charge du navigateur, ou de résoudre ce problème au niveau de la couche application, pour utiliser la logique du serveur Web.

Si vous utilisez Apache et que vous pouvez placer un fichier .htaccess dans le répertoire approprié, vous pouvez utiliser le code ci-dessous. Bien sûr, vous pouvez également le mettre dans httpd.conf, si vous y avez accès.

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

La directive FilesMatch est juste une expression régulière, elle peut donc être définie aussi précisément que vous le souhaitez, ou vous pouvez ajouter d'autres extensions.

La ligne Header fait la même chose que la première ligne des scripts PHP ci-dessus. Si vous devez également définir les lignes Content-Type, vous pouvez le faire de la même manière, mais je n'ai pas trouvé cela nécessaire.

Evan Donovan
la source
0

J'ai résolu le mien en utilisant toute l'url du fichier PDF (au lieu de simplement mettre le nom ou l'emplacement du fichier à href): a href = "domain. Com / pdf / filename.pdf"

Mark Allena
la source
0

si vous devez limiter le taux de téléchargement, utilisez ce 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!');
}

?>

Pour plus d'informations cliquez ici

Mehran Hooshangi
la source
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
la source
1
Vous devez expliquer ce que vous avez fourni dans votre code. Même sous forme courte si cela n'est pas possible dans les détails ou pas requis dans les détails.
Suraj Kumar
-1

Dans une application Ruby on Rails (en particulier avec quelque chose comme la gemme Prawn et le plugin Prawnto Rails), vous pouvez accomplir cela un peu plus simplement qu'un script complet (comme l'exemple PHP précédent).

Dans votre contrôleur:

def index

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

La partie render: layout => false indique au navigateur d'ouvrir la fenêtre "Voulez-vous télécharger ce fichier?" au lieu d'essayer de rendre le PDF. Ensuite, vous pourrez créer un lien vers le fichier normalement: http://monsite.com/myawesomepdf.pdf

btw
la source
Où écrire ce code? quel contrôleur, je suis nouveau sur PHP s'il vous plaît expliquer.
Prashant
@Prashant Ce n'est pas PHP, c'est Ruby on Rails.
eloyesp
@Prashant: Si vous avez besoin d'un exemple PHP, regardez ceux plus haut dans le fil de discussion, mais veuillez lire les commentaires sur la façon dont ils pourraient ne pas être sécurisés.
Evan Donovan