Bagaimana cara membuat file PDF dapat diunduh dalam tautan HTML?

124

Saya memberikan tautan file pdf di halaman web saya untuk diunduh, seperti di bawah ini

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

Masalahnya adalah ketika pengguna mengklik tautan ini

  • Jika pengguna telah menginstal Adobe Acrobat, maka itu membuka file di jendela browser yang sama di Adobe Reader.
  • Jika Adobe Acrobat tidak diinstal maka itu akan muncul ke pengguna untuk Mengunduh file.

Tapi saya ingin itu selalu pop-up kepada pengguna untuk diunduh, terlepas dari "Adobe acrobat" diinstal atau tidak.

Tolong beritahu saya bagaimana saya bisa melakukan ini?

Prashant
sumber

Jawaban:

116

Alih-alih menautkan ke file .PDF, lakukan sesuatu seperti

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

yang mengeluarkan tajuk khusus, membuka PDF (brankas biner) dan mencetak data ke browser pengguna, kemudian mereka dapat memilih untuk menyimpan PDF meskipun pengaturan browser mereka. Pdf_server.php akan terlihat seperti ini:

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: dan jelas jalankan beberapa pemeriksaan kewarasan pada variabel "file" untuk mencegah orang mencuri file Anda seperti tidak menerima ekstensi file, menolak garis miring, menambahkan .pdf ke nilai

TravisO
sumber
2
Saya menghadapi masalah lain dengan ini, bahwa file saya terletak di /products/brochure/myfile.pdf Saya memberikan variabel $ file sebagai $ file_path = $ _SERVER ['DOCUMENT_ROOT']. '/ Products / brochure /'. $ file; tetapi mengunduh file sebagai "% 2Fvar% 2Fwww% 2Fweb15% 2Fweb% 2Fproducts% 2Fbrochure% 2myfile.pdf"
Prashant
3
@TravisO "Content-type: application/force-download"tidak terdaftar di mana pun di sini: iana.org/assignments/media-types/application Ini adalah header yang sepenuhnya palsu. Harap jangan membuat header dan mengirimkannya. Bisakah Anda memperbarui jawaban Anda. Terima kasih.
Nicholas Wilson
4
Berhati-hatilah saat menggunakan kode ini kata demi kata. Ini memperkenalkan kerentanan LFI yang serius, saat Anda meneruskan variabel GET langsung ke fopen.
Joost
4
Kode ini mungkin berbahaya dengan cara lain. Jika Anda mengirimkan tautan HTTP ke fopen, saya pikir itu akan mengambil file dari server lain. Dapat digunakan oleh penyerang untuk mencoba memindai jaringan internal Anda untuk menemukan file PDF yang terekspos.
Rory McCune
2
Belum lagi betapa mudahnya untuk melewati "pemeriksaan kesehatan" yang menurut Anda akan Anda lakukan pada parameter "file". Kemungkinan besar serangan path injection / directory traversal.
AviD
210

Ini adalah masalah umum tetapi hanya sedikit orang yang tahu bahwa ada solusi HTML 5 sederhana:

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

Di mana newfilenamenama file yang disarankan bagi pengguna untuk menyimpan file. Atau akan default ke nama file di sisi server jika Anda membiarkannya kosong, seperti ini:

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

Kompatibilitas: Saya menguji ini di Firefox 21 dan Iron, keduanya berfungsi dengan baik. Ini mungkin tidak berfungsi pada browser yang tidak kompatibel dengan HTML5 atau sudah usang. Satu-satunya browser yang saya uji yang tidak memaksa unduhan adalah IE ...

Periksa kompatibilitas di sini: http://caniuse.com/#feat=download

T_D
sumber
9
Ini adalah solusi sederhana tetapi sayangnya tidak didukung secara luas, khususnya. tidak mendukung oleh IE caniuse.com/#feat=download
benebun
2
Ya, saya tahu benar. Itu sebabnya saya memiliki catatan samping tentang kompatibilitas. Dan menurut sumber Anda baik IE dan Safari tidak mendukung pendekatan ini, atau setidaknya belum :) Bagaimanapun, jika Anda ingin semua browser memaksa mengunduh, saya sarankan untuk memeriksa beberapa jawaban lain sebagai gantinya ...
T_D
3
bekerja seperti pesona dengan chrome Versi 39.0.2171.65 (64-bit)!
edelans
Solusinya mudah tapi sayangnya tidak didukung di IE dan Safari.
valkirilov
mengatakan tidak ada izin untuk mengakses
Hackbal Teamz
50

Jangan mengulang setiap baris file. Gunakan readfile sebagai gantinya, lebih cepat. Ini dari situs 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;
}

Pastikan untuk membersihkan variabel get Anda karena seseorang dapat mengunduh beberapa file php ...

Alex V.
sumber
4
Fungsi readfile memang lebih cepat. Saya pribadi merekomendasikan menggunakan jawaban ini daripada yang diterima
Jose Garrido
24

Alih-alih menggunakan skrip PHP, untuk membaca dan membersihkan file, lebih baik menulis ulang header menggunakan .htaccess. Ini akan membuat URL "bagus" ( myfile.pdfbukan download.php?myfile).

<FilesMatch "\.pdf$">
ForceType applicaton/octet-stream
Header set Content-Disposition attachment
</FilesMatch>
Rob W.
sumber
Bukankah ini akan membuat SEMUA PDF Anda diunduh secara paksa?
TecBrat
1
@TecBrat Ya, tapi itu yang diminta OP. Jika Anda ingin membatasi ke beberapa PDF saja, edit ^\.pdf$ekspresi reguler.
Rob W
1
@TecBrat atau letakkan file .htaccess hanya di subfolder di mana perilaku ini diperlukan.
habakuk
14

Saya menemukan cara untuk melakukannya dengan HTML lama biasa dan JavaScript / jQuery yang terdegradasi dengan baik. Diuji di IE7-10, Safari, Chrome, dan FF:

HTML untuk tautan unduhan:

<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 (kode JavaScript murni akan lebih bertele-tele) yang mensimulasikan mengklik tautan setelah sedikit penundaan:

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

Untuk membuatnya lebih kuat Anda bisa menambahkan deteksi fitur HTML5 dan jika tidak ada maka gunakan window.open()untuk membuka jendela baru dengan file tersebut.

Alex W.
sumber
5

Inilah kuncinya:

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

Aplikasi tipe konten / x-pdf-document atau application / pdf dikirim saat mengirim file PDF. Adobe Reader biasanya menyetel penangan untuk jenis MIME ini sehingga browser akan meneruskan dokumen ke Adobe Reader ketika salah satu jenis PDF MIME diterima.

Def tiba-tiba
sumber
3

Saya tahu saya sangat terlambat untuk menjawab ini tetapi saya menemukan sebuah hack untuk melakukan ini dalam javascript.

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

Coba ini:

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

Kode di dalam pdf_server_with_path.php adalah:

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
sumber
0

Inilah pendekatan yang berbeda. Saya lebih suka daripada mengandalkan dukungan browser, atau mengatasinya di lapisan aplikasi, untuk menggunakan logika server web.

Jika Anda menggunakan Apache, dan dapat meletakkan file .htaccess di direktori yang relevan, Anda dapat menggunakan kode di bawah ini. Tentu saja, Anda dapat meletakkan ini di httpd.conf juga, jika Anda memiliki akses ke sana.

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

Direktif FilesMatch hanyalah sebuah regex sehingga dapat disetel sedetail yang Anda inginkan, atau Anda dapat menambahkan ekstensi lain.

Baris Header melakukan hal yang sama seperti baris pertama pada skrip PHP di atas. Jika Anda perlu mengatur baris Content-Type juga, Anda dapat melakukannya dengan cara yang sama, tetapi saya tidak merasa itu perlu.

Evan Donovan
sumber
0

Saya menyelesaikan milik saya menggunakan seluruh url file PDF (Alih-alih hanya meletakkan nama file atau lokasi ke href): a href = "domain. Com / pdf / filename.pdf"

Mark Allena
sumber
0

jika Anda perlu membatasi kecepatan unduhan, gunakan kode ini !!

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

?>

Untuk informasi lebih lanjut klik disini

Mehran Hooshangi
sumber
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>   
pengguna11021789
sumber
1
Anda harus menjelaskan apa yang telah Anda berikan dalam kode Anda. Bahkan dalam bentuk singkat jika tidak memungkinkan secara detil atau tidak diperlukan secara detil.
Suraj Kumar
-1

Dalam aplikasi Ruby on Rails (terutama dengan sesuatu seperti Prawn gem dan plugin Prawnto Rails), Anda dapat melakukannya sedikit lebih sederhana daripada full on script (seperti contoh PHP sebelumnya).

Di pengontrol Anda:

def index

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

Bagian render: layout => false memberi tahu browser untuk membuka "Apakah Anda ingin mengunduh file ini?" meminta alih-alih mencoba merender PDF. Kemudian Anda dapat menautkan ke file tersebut secara normal: http://mysite.com/myawesomepdf.pdf

btw
sumber
Di mana menulis kode ini? yang controller, saya baru mengenal PHP tolong jelaskan.
Prashant
@Prashant Ini bukan PHP, ini Ruby on Rails.
eloyesp
@Prashant: Jika Anda memerlukan contoh PHP, lihat yang lebih jauh di utas, tetapi harap baca komentar tentang bagaimana mereka bisa merasa tidak aman.
Evan Donovan