Làm cách nào để tải xuống tệp PDF trong liên kết HTML?


124

Tôi đang cung cấp liên kết của một tệp pdf trên trang web của tôi để tải xuống, như dưới đây

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

Vấn đề là khi người dùng nhấp vào liên kết này thì

  • Nếu người dùng đã cài đặt Adobe Acrobat, thì nó sẽ mở tệp trong cùng cửa sổ trình duyệt trong Adobe Reader.
  • Nếu Adobe Acrobat chưa được cài đặt thì nó sẽ bật lên cho người dùng Tải xuống tệp.

Nhưng tôi muốn nó luôn bật lên cho người dùng tải xuống, bất kể "Adobe acrobat" có được cài đặt hay không.

Xin vui lòng cho tôi biết làm thế nào tôi có thể làm điều này?

Câu trả lời:


115

Thay vì liên kết đến tệp .PDF, thay vào đó hãy làm một cái gì đó như

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

xuất ra một tiêu đề tùy chỉnh, mở tệp PDF (an toàn nhị phân) và in dữ liệu lên trình duyệt của người dùng, sau đó họ có thể chọn lưu tệp PDF bất chấp cài đặt trình duyệt của họ. Pdf_server.php sẽ trông như thế này:

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: và rõ ràng chạy một số kiểm tra độ tỉnh táo trên biến "tệp" để ngăn mọi người ăn cắp tệp của bạn như không chấp nhận tiện ích mở rộng tệp, từ chối dấu gạch chéo, thêm .pdf vào giá trị


2
Tôi đang phải đối mặt với một vấn đề khác, đó là tệp của tôi được đặt tại /products/brochure/myfile.pdf Tôi đang đưa ra biến tệp $ là $ file_path = $ _SERVER ['DOCUMENT_ROOT']. '/ Products / brochure /'. $ tập tin; nhưng nó tải xuống tệp dưới dạng "% 2Fvar% 2Fwww% 2Fweb15% 2Fweb% 2Fproducts% 2Fbrochure% 2myfile.pdf"
Prashant

3
@TravisO "Content-type: application/force-download"không được liệt kê ở bất cứ đâu tại đây: iana.org/assignments/media-types/application Đó là một tiêu đề hoàn toàn không có thật. Xin đừng trang điểm và gửi chúng. Bạn có thể cập nhật câu trả lời của bạn. Cảm ơn.
Nicholas Wilson

4
Hãy cẩn thận khi sử dụng mã này nguyên văn, mặc dù. Điều này giới thiệu một lỗ hổng LFI nghiêm trọng, khi bạn chuyển trực tiếp các biến GET fopen.
Joost

4
Mã này có khả năng nguy hiểm theo một cách khác. Nếu bạn chuyển các liên kết HTTP đến fopen, tôi nghĩ rằng nó sẽ truy xuất tệp từ một máy chủ khác. Kẻ tấn công có thể sử dụng để quét mạng nội bộ của bạn để tìm các tệp PDF bị lộ.
Rory McCune

2
Không đề cập đến việc dễ dàng vượt qua bất kỳ "kiểm tra vệ sinh" nào mà bạn nghĩ rằng bạn sẽ thực hiện với tham số "tệp". Đường dẫn tiêm / thư mục tấn công ngang ngược là rất có thể.
AviD

209

Đây là một vấn đề phổ biến nhưng ít người biết có một giải pháp HTML 5 đơn giản:

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

Trong trường hợp newfilenamelà tên file gợi ý cho người sử dụng để lưu các tập tin. Hoặc nó sẽ mặc định là tên tệp trên máy chủ nếu bạn để trống, như thế này:

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

Khả năng tương thích: Tôi đã thử nghiệm điều này trên Firefox 21 và Iron, cả hai đều hoạt động tốt. Nó có thể không hoạt động trên các trình duyệt không tương thích hoặc lỗi thời HTML5. Trình duyệt duy nhất tôi đã kiểm tra không bắt buộc tải xuống là IE ...

Kiểm tra tính tương thích tại đây: http://caniuse.com/#feat=d Download


9
Đây là một giải pháp đơn giản nhưng tiếc là không được hỗ trợ rộng rãi, đặc biệt. không có hỗ trợ bởi IE caniuse.com/#feat=doad
benebun

2
Đúng, tôi biết đúng. Đó là lý do tại sao tôi có lưu ý phụ về khả năng tương thích. Và theo nguồn của bạn, cả IE và Safari đều không hỗ trợ phương pháp này, hoặc ít nhất là chưa :) Dù sao đi nữa, nếu bạn muốn tất cả các trình duyệt bắt buộc tải xuống, tôi khuyên bạn nên kiểm tra một số câu trả lời khác thay thế ...
T_D

3
hoạt động như một bùa mê với phiên bản chrome 39.0.2171,65 (64-bit)!
edelans

Giải pháp rất dễ nhưng rất tiếc không được hỗ trợ trong IE và Safari.
valkirilov

nói không được phép truy cập
Hackbal Teamz

50

Đừng lặp qua từng dòng tệp. Sử dụng readfile thay thế, nó nhanh hơn. Đây là trang web php: http://php.net/manual/en/feft.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;
}

Đảm bảo vệ sinh biến get của bạn vì ai đó có thể tải xuống một số tệp php ...


4
Hàm readfile thực sự nhanh hơn. Cá nhân tôi khuyên bạn nên sử dụng câu trả lời này thay vì câu trả lời được chấp nhận
Jose Garrido

24

Thay vì sử dụng tập lệnh PHP, để đọc và xóa tập tin, việc viết lại tiêu đề sẽ gọn gàng hơn .htaccess. Điều này sẽ giữ một URL "đẹp" ( myfile.pdfthay vì download.php?myfile).

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

Điều này sẽ không làm cho TẤT CẢ các tệp PDF của bạn buộc phải tải xuống?
TecBrat

1
@TecBrat Có, nhưng đó là những gì OP yêu cầu. Nếu bạn muốn giới hạn chỉ một vài tệp PDF, hãy chỉnh sửa ^\.pdf$biểu thức thông thường.
Rob W

1
@TecBrat hoặc chỉ đặt tệp .htaccess trong thư mục con nơi hành vi này là cần thiết.
habakuk

14

Tôi đã tìm thấy một cách để làm điều đó với HTML và JavaScript / jQuery cũ đơn giản, xuống cấp một cách duyên dáng. Đã thử nghiệm trong IE7-10, Safari, Chrome và FF:

HTML cho liên kết tải xuống:

<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 (mã JavaScript thuần sẽ dài dòng hơn) mô phỏng nhấp vào liên kết sau một độ trễ nhỏ:

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

Để làm cho điều này mạnh mẽ hơn, bạn có thể thêm tính năng phát hiện HTML5 và nếu không có thì hãy sử dụng window.open()để mở một cửa sổ mới với tệp.


5

Đây là chìa khóa:

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

Ứng dụng loại nội dung / x-pdf-document hoặc application / pdf được gửi trong khi gửi tệp PDF. Adobe Reader thường đặt trình xử lý cho loại MIME này để trình duyệt sẽ chuyển tài liệu tới Adobe Reader khi nhận được bất kỳ loại MIME PDF nào.


3

Tôi biết tôi rất muộn để trả lời điều này nhưng tôi đã tìm thấy một hack để làm điều này trong javascript.

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

0

Thử cái này:

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

Mã bên trong pdf_server_with_path.php là:

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

Đây là một cách tiếp cận khác nhau. Tôi thích thay vì dựa vào hỗ trợ trình duyệt, hoặc giải quyết vấn đề này ở lớp ứng dụng, để sử dụng logic máy chủ web.

Nếu bạn đang sử dụng Apache và có thể đặt tệp .htaccess vào thư mục có liên quan, bạn có thể sử dụng mã bên dưới. Tất nhiên, bạn cũng có thể đặt cái này trong httpd.conf, nếu bạn có quyền truy cập vào đó.

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

Lệnh FilesMatch chỉ là một biểu thức chính quy để nó có thể được đặt chi tiết như bạn muốn hoặc bạn có thể thêm vào các tiện ích mở rộng khác.

Dòng Header thực hiện tương tự như dòng đầu tiên trong các tập lệnh PHP ở trên. Nếu bạn cũng cần thiết lập các dòng Kiểu nội dung, bạn có thể làm như vậy theo cách tương tự, nhưng tôi không thấy cần thiết.


0

Tôi đã giải quyết vấn đề của mình bằng cách sử dụng toàn bộ url của tệp PDF (Thay vì chỉ đặt tên tệp hoặc vị trí thành href): a href = "domain. Com / pdf / filename.pdf"


0

nếu bạn cần giới hạn tốc độ tải xuống, hãy sử dụng mã này !!

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

?>

Để biết thêm thông tin bấm vào đây


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
Bạn nên giải thích những gì bạn đã cung cấp trong mã của bạn. Ngay cả ở dạng ngắn nếu không thể chi tiết hoặc không yêu cầu chi tiết.
Suraj Kumar

-1

Trong ứng dụng Ruby on Rails (đặc biệt là với thứ gì đó như gem Prawn và plugin Prawnto Rails), bạn có thể thực hiện việc này đơn giản hơn một chút so với tập lệnh đầy đủ (như ví dụ PHP trước đây).

Trong bộ điều khiển của bạn:

def index

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

Phần kết xuất: layout => false sẽ cho trình duyệt mở ra "Bạn có muốn tải xuống tệp này không?" nhắc thay vì cố gắng kết xuất PDF. Sau đó, bạn sẽ có thể liên kết đến tệp một cách bình thường: http://mysite.com/myawesomepdf.pdf


Nơi để viết mã này? Bộ điều khiển nào, tôi mới sử dụng PHP hãy giải thích.
Prashant

@Prashant Đây không phải là PHP, đó là Ruby on Rails.
eloyesp

@Prashant: Nếu bạn cần một ví dụ về PHP, hãy xem các ví dụ tiếp theo về chủ đề, nhưng vui lòng đọc các nhận xét về cách chúng có thể không an toàn.
Evan Donovan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.