HTML 링크에서 PDF 파일을 다운로드 가능하게 만드는 방법은 무엇입니까?


124

내 웹 페이지에 다운로드 할 pdf 파일 링크를 아래와 같이 제공합니다.

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

문제는 사용자가이 링크를 클릭하면

  • 사용자가 Adobe Acrobat을 설치 한 경우 Adobe Reader의 동일한 브라우저 창에서 파일을 엽니 다.
  • Adobe Acrobat이 설치되어 있지 않으면 파일 다운로드를 위해 사용자에게 팝업됩니다.

하지만 "Adobe acrobat"의 설치 여부와 관계없이 항상 다운로드를 위해 사용자에게 팝업으로 표시되기를 바랍니다.

어떻게 할 수 있는지 알려주세요.

답변:


116

.PDF 파일에 연결하는 대신 다음과 같은 작업을 수행하십시오.

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

사용자 정의 헤더를 출력하고 PDF (바이너리 세이프)를 열고 사용자의 브라우저에 데이터를 인쇄 한 다음 브라우저 설정에 관계없이 PDF를 저장하도록 선택할 수 있습니다. pdf_server.php는 다음과 같습니다.

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); 

추신 : 그리고 분명히 "파일"변수에 대해 몇 가지 온 전성 검사를 실행하여 파일 확장명 허용 안 함, 슬래시 거부, 값에 .pdf 추가와 같은 파일을 훔치는 것을 방지합니다.


2
내 파일이 /products/brochure/myfile.pdf에 있다는 또 다른 문제에 직면하고 있습니다. $ file 변수를 $ file_path = $ _SERVER [ 'DOCUMENT_ROOT']. '/ products / brochure /'로 지정하고 있습니다. $ file; 하지만이 같은 파일을 다운로드 "% 2Fvar % 2Fwww % 2Fweb15 % 2Fweb % 2Fproducts % 2Fbrochure % 2myfile.pdf"
— Prashant

3
@TravisO "Content-type: application/force-download"는 여기 어디에도 나와 있지 않습니다 : iana.org/assignments/media-types/application 그것은 완전히 가짜 헤더입니다. 헤더를 만들어 보내지 마세요. 답변을 업데이트 해 주시겠습니까? 감사.
— Nicholas Wilson

4
하지만이 코드를 그대로 사용할 때는주의하십시오. GET 변수를 .NET Framework로 직접 전달하므로 심각한 LFI 취약성이 발생 fopen합니다.
— Joost

4
이 코드는 다른 방식으로 위험 할 수 있습니다. HTTP 링크를 fopen에 전달하면 다른 서버에서 파일을 검색 할 것이라고 생각합니다. 공격자가 노출 된 PDF 파일에 대해 내부 네트워크를 스캔하는 데 사용할 수 있습니다.
— Rory McCune 2015 년

2
"파일"매개 변수에 대해 수행 할 것이라고 생각하는 "건전성 검사"를 우회하는 것이 얼마나 쉬운지는 말할 것도 없습니다. 경로 삽입 / 디렉토리 탐색 공격은 매우 가능성이 높습니다.
— AviD

209

이것은 일반적인 문제이지만 간단한 HTML 5 솔루션이 있다는 것을 아는 사람은 거의 없습니다.

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

newfilename사용자가 파일을 저장하기 위해 제안 된 파일 이름은 어디에 있습니까 ? 또는 다음과 같이 비워두면 서버 측의 파일 이름이 기본값으로 설정됩니다.

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

호환성 : Firefox 21과 Iron에서 테스트했는데 둘 다 잘 작동했습니다. HTML5와 호환되지 않거나 오래된 브라우저에서는 작동하지 않을 수 있습니다. 다운로드를 강요하지 않은 유일한 브라우저는 IE입니다.

여기에서 호환성 확인 : http://caniuse.com/#feat=download


9
이것은 간단한 해결책이지만 안타깝게도 널리 지원되지는 않습니다. 어떤 IE에 의해 지원 caniuse.com/#feat=download
— benebun

2
네, 맞습니다. 그렇기 때문에 호환성에 대한 참고 사항이 있습니다. 그리고 귀하의 소스에 따르면 IE와 Safari 모두이 접근 방식을 지원하지 않거나 적어도 아직 지원하지 않습니다 :) 어쨌든 모든 브라우저가 강제로 다운로드하도록하려면 다른 답변을 대신 확인하는 것이 좋습니다 ...
— T_D

3
크롬 버전 39.0.2171.65 (64 비트)에서 매력처럼 작동합니다!
— edelans

이 솔루션은 쉽지만 불행히도 IE와 Safari에서는 지원되지 않습니다.
— valkirilov

액세스에 아무런 권한이 말한다
— Hackbal Teamz

50

모든 파일 행을 반복하지 마십시오 . 대신 readfile 을 사용하십시오 . 이것은 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;
}

누군가가 일부 PHP 파일을 다운로드 할 수 있으므로 get 변수를 삭제하십시오.


4
에서 ReadFile 기능은 참으로 빠릅니다. 개인적으로 허용 한 대신이 답변을 사용하는 것이 좋습니다
— 호세 가리도에게

24

PHP 스크립트를 사용하는 대신 파일을 읽고 플러시하려면 .NET을 사용하여 헤더를 다시 작성하는 것이 더 깔끔합니다 .htaccess. 이렇게하면 "좋은"URL ( myfile.pdf대신 download.php?myfile) 이 유지됩니다 .

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

이로 인해 모든 PDF가 강제로 다운로드되지 않습니까?
— TecBrat 2014 년

1
@TecBrat 예, 그러나 OP가 요청한 것입니다. 몇 개의 PDF로만 제한하려면 ^\.pdf$정규식을 편집하십시오 .
— Rob W

1
@TecBrat 또는이 동작이 필요한 하위 폴더에만 .htaccess 파일을 넣으십시오.
— habakuk 2015 년

14

우아하게 저하되는 평범한 HTML과 JavaScript / jQuery로 수행하는 방법을 찾았습니다. IE7-10, Safari, Chrome 및 FF에서 테스트되었습니다.

다운로드 링크 용 HTML :

<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 (순수한 JavaScript 코드가 더 장황함) :

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

이를 더 강력하게 만들기 위해 HTML5 기능 감지를 추가 할 수 있으며없는 경우 window.open()파일로 새 창을 여는 데 사용 합니다.


5

이것이 핵심입니다.

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

PDF 파일을 보내는 동안 Content-type application / x-pdf-document 또는 application / pdf가 전송됩니다. Adobe Reader는 일반적으로이 MIME 유형에 대한 핸들러를 설정하므로 PDF MIME 유형이 수신 될 때 브라우저가 문서를 Adobe Reader에 전달합니다.


3

나는 이것에 대해 매우 늦었지만 자바 스크립트에서 이것을 할 수있는 해킹을 발견했습니다.

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

0

이 시도:

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

pdf_server_with_path.php의 코드는 다음과 같습니다.

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

여기에 다른 접근 방식이 있습니다. 웹 서버 로직을 사용하기 위해 브라우저 지원에 의존하거나 애플리케이션 계층에서이를 해결하는 것보다 선호합니다.

Apache를 사용하고 있고 관련 디렉토리에 .htaccess 파일을 넣을 수 있다면 아래 코드를 사용할 수 있습니다. 물론 액세스 권한이 있다면 httpd.conf에도 넣을 수 있습니다.

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

FilesMatch 지시문은 정규식이므로 원하는대로 세밀하게 설정하거나 다른 확장을 추가 할 수 있습니다.

헤더 행은 위의 PHP 스크립트의 첫 번째 행과 동일한 작업을 수행합니다. Content-Type 행도 설정해야하는 경우 동일한 방식으로 설정할 수 있지만 필요한 것을 찾지 못했습니다.


0

파일 이름이나 위치를 href에 넣는 대신 PDF 파일의 전체 URL을 사용하여 내 문제를 해결했습니다. a href = "domain. com / pdf / filename.pdf"


0

다운로드 속도를 제한해야한다면이 코드를 사용하세요 !!

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

?>

자세한 내용은 여기를 클릭하세요


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
코드에서 제공 한 내용을 설명해야합니다. 세부 사항이 불가능하거나 세부 사항이 필요하지 않은 경우 짧은 형식으로도 가능합니다.
— Suraj Kumar

-1

Ruby on Rails 애플리케이션 (특히 Prawn gem 및 Prawnto Rails 플러그인과 같은 것)에서 전체 스크립트 (이전 PHP 예제와 같이)보다 조금 더 간단하게이를 수행 할 수 있습니다.

컨트롤러에서 :

def index

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

render : layout => false 부분은 브라우저에 "이 파일을 다운로드 하시겠습니까?"라는 메시지를 표시합니다. PDF를 렌더링하는 대신 프롬프트가 표시됩니다. 그러면 정상적으로 파일에 연결할 수 있습니다. http://mysite.com/myawesomepdf.pdf


이 코드를 어디에 작성 했습니까? 어떤 컨트롤러, 나는 PHP를 처음 접했습니다.
— Prashant 2008-12-13

@Prashant 이것은 PHP가 아니라 Ruby on Rails입니다.
— eloyesp 2013

@Prashant : PHP 예제가 필요한 경우 스레드의 상위 항목을 살펴 보지만 안전하지 않을 수있는 방법에 대한 의견을 읽어보십시오.
— Evan Donovan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.