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.