Jak sprawić, by plik PDF był dostępny do pobrania w linku HTML?


124

Podaję link do pliku pdf na mojej stronie do pobrania, jak poniżej

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

Problem występuje wtedy, gdy użytkownik kliknie ten link

  • Jeśli użytkownik zainstalował program Adobe Acrobat, otwiera plik w tym samym oknie przeglądarki w programie Adobe Reader.
  • Jeśli program Adobe Acrobat nie jest zainstalowany, pojawi się wyskakujące okienko dla użytkownika w celu pobrania pliku.

Ale chcę, aby użytkownik zawsze wyświetlał wyskakujące okienko do pobrania, niezależnie od tego, czy program „Adobe Acrobat” jest zainstalowany, czy nie.

Powiedz mi, jak mam to zrobić?

Odpowiedzi:


116

Zamiast tworzyć link do pliku .PDF, zrób coś w rodzaju

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

który wyświetla niestandardowy nagłówek, otwiera plik PDF (sejf binarny) i drukuje dane do przeglądarki użytkownika, a następnie może wybrać zapisanie pliku PDF pomimo ustawień przeglądarki. Plik pdf_server.php powinien wyglądać następująco:

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: i oczywiście przeprowadź kontrolę poprawności zmiennej "plik", aby zapobiec kradzieży twoich plików, np. Nie akceptuj rozszerzeń plików, odmawiaj ukośników, dodaj .pdf do wartości


2
Mam inny problem z tym, że mój plik znajduje się w /products/brochure/myfile.pdf Podaję zmienną $ file jako $ file_path = $ _SERVER ['DOCUMENT_ROOT']. '/ Products / brochure /'. $ plik; ale pobiera plik jako „% 2Fvar% 2Fwww% 2Fweb15% 2Fweb% 2Fproducts% 2Fbrochure% 2myfile.pdf”
Prashant,

3
@TravisO "Content-type: application/force-download"nie jest nigdzie tutaj wymienione: iana.org/ assignments / media-types / application To całkowicie fałszywy nagłówek. Nie twórz nagłówków i nie wysyłaj ich. Czy mógłbyś zaktualizować swoją odpowiedź. Dzięki.
Nicholas Wilson

4
Uważaj jednak, używając tego kodu dosłownie. Wprowadza to poważną lukę w zabezpieczeniach LFI, ponieważ przekazujesz zmienne GET bezpośrednio do fopen.
Joost

4
Ten kod jest prawdopodobnie niebezpieczny z innego powodu. Jeśli przekażesz linki HTTP do fopen, myślę, że pobierze plik z innego serwera. Może zostać wykorzystane przez osobę atakującą do próby przeskanowania sieci wewnętrznej w poszukiwaniu ujawnionych plików PDF.
Rory McCune

2
Nie wspominając już o tym, jak łatwo byłoby ominąć wszelkie „testy poprawności”, które Twoim zdaniem wykonasz dla parametru „plik”. Ataki polegające na wstrzyknięciu ścieżki / przejściu przez katalog są bardzo prawdopodobne.
AviD

209

To częsty problem, ale niewiele osób wie, że istnieje proste rozwiązanie HTML 5:

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

Gdzie newfilenamejest sugerowana nazwa pliku dla użytkownika do zapisania pliku. Lub domyślnie zostanie wybrana nazwa pliku na serwerze, jeśli pozostawisz ją pustą, na przykład:

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

Kompatybilność: przetestowałem to na Firefox 21 i Iron, oba działały dobrze. Może nie działać w przeglądarkach niezgodnych z HTML5 lub nieaktualnych. Jedyną przeglądarką, którą przetestowałem, która nie wymuszała pobierania, jest IE ...

Sprawdź zgodność tutaj: http://caniuse.com/#feat=download


9
Jest to proste rozwiązanie, ale niestety niezbyt szeroko obsługiwane, zwł. brak wsparcia przez IE caniuse.com/#feat=download
benebun

2
Tak, wiem dobrze. Dlatego mam dodatkową uwagę na temat kompatybilności. I według twojego źródła zarówno IE, jak i Safari nie obsługują tego podejścia, a przynajmniej jeszcze nie :) W każdym razie, jeśli chcesz, aby wszystkie przeglądarki wymuszały pobieranie, sugeruję sprawdzenie niektórych innych odpowiedzi ...
T_D

3
działa jak urok dzięki Chrome w wersji 39.0.2171.65 (64-bitowej)!
edelans

Rozwiązanie jest łatwe, ale niestety nie jest obsługiwane w IE i Safari.
valkirilov

mówi, że nie ma pozwolenia na dostęp
Hackbal Teamz

50

Nie przechodź przez każdą linię pliku. Zamiast tego użyj readfile , jest szybszy. To jest poza witryną 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;
}

Upewnij się, że wyczyściłeś swoją zmienną get, ponieważ ktoś mógłby pobrać niektóre pliki php ...


4
Funkcja readfile jest rzeczywiście szybsza. Osobiście zalecam użycie tej odpowiedzi zamiast zaakceptowanej
Jose Garrido

24

Zamiast używać skryptu PHP do odczytu i opróżnienia pliku, lepiej jest przepisać nagłówek za pomocą .htaccess. Pozwoli to zachować „ładny” adres URL ( myfile.pdfzamiast download.php?myfile).

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

Czy nie spowodowałoby to wymuszenia pobierania WSZYSTKICH plików PDF?
TecBrat

1
@TecBrat Tak, ale o to prosił OP. Jeśli chcesz ograniczyć tylko do kilku plików PDF, edytuj ^\.pdf$wyrażenie regularne.
Rob W

1
@TecBrat lub umieść plik .htaccess tylko w podfolderze, w którym to zachowanie jest potrzebne.
habakuk

14

Znalazłem sposób, aby to zrobić za pomocą zwykłego starego HTML i JavaScript / jQuery, które z wdziękiem degradują się. Testowane w IE7-10, Safari, Chrome i FF:

Link HTML do pobrania:

<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 (czysty kod JavaScript byłby bardziej rozwlekły), który symuluje kliknięcie linku po niewielkim opóźnieniu:

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

Aby uczynić to bardziej niezawodnym, możesz dodać wykrywanie funkcji HTML5, a jeśli go tam nie ma, użyj, window.open()aby otworzyć nowe okno z plikiem.


5

To jest klucz:

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

Aplikacja typu content / x-pdf-document lub application / pdf jest wysyłana podczas wysyłania pliku PDF. Adobe Reader zwykle ustawia program obsługi dla tego typu MIME, więc przeglądarka przekaże dokument do programu Adobe Reader po odebraniu dowolnego typu PDF MIME.


3

Wiem, że jestem bardzo spóźniony, aby odpowiedzieć na to pytanie, ale znalazłem sposób, aby to zrobić w javascript.

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

0

Spróbuj tego:

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

Kod wewnątrz pdf_server_with_path.php to:

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

Oto inne podejście. Wolę raczej niż polegać na obsłudze przeglądarki lub zajmować się tym w warstwie aplikacji, używać logiki serwera WWW.

Jeśli używasz Apache i możesz umieścić plik .htaccess w odpowiednim katalogu, możesz użyć poniższego kodu. Oczywiście możesz również umieścić to w httpd.conf, jeśli masz do tego dostęp.

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

Dyrektywa FilesMatch jest po prostu wyrażeniem regularnym, więc można ją ustawić tak szczegółowo, jak chcesz, lub dodać inne rozszerzenia.

Linia nagłówka robi to samo, co pierwsza linia w powyższych skryptach PHP. Jeśli potrzebujesz ustawić również linie Content-Type, możesz to zrobić w ten sam sposób, ale nie uznałem tego za konieczne.


0

Rozwiązałem mój, używając całego adresu URL pliku PDF (zamiast umieszczać nazwę pliku lub lokalizację w href): a href = "domena. Com / pdf / nazwa pliku.pdf"


0

jeśli chcesz ograniczyć prędkość pobierania, użyj tego kodu !!

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

?>

Aby uzyskać więcej informacji, kliknij tutaj


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
Powinieneś wyjaśnić, co podałeś w swoim kodzie. Nawet w krótkiej formie, jeśli nie jest to możliwe w szczegółach lub nie jest wymagane w szczegółach.
Suraj Kumar

-1

W aplikacji Ruby on Rails (zwłaszcza w przypadku czegoś takiego jak klejnot Prawn i wtyczka Prawnto Rails) możesz to zrobić w nieco prostszy sposób niż pełny skrypt (jak w poprzednim przykładzie PHP).

W kontrolerze:

def index

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

Część render: layout => false mówi przeglądarce, aby otworzyła okno „Czy chcesz pobrać ten plik?” zamiast próbować renderować plik PDF. Wtedy możesz normalnie połączyć się z plikiem: http://mysite.com/myawesomepdf.pdf


Gdzie napisać ten kod? który kontroler, jestem nowy w PHP, proszę wyjaśnić.
Prashant

@Prashant To nie jest PHP, to Ruby on Rails.
eloyesp

@Prashant: Jeśli potrzebujesz przykładu PHP, spójrz na te w dalszej części wątku, ale przeczytaj komentarze o tym, jak mogą być niepewne.
Evan Donovan
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.