Как сделать PDF-файл загружаемым по HTML-ссылке?

Я даю ссылку на файл pdf на своей веб-странице для загрузки, как показано ниже

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

Проблема в том, что когда пользователь нажимает на эту ссылку,

  • Если пользователь установил Adobe Acrobat, он открывает файл в том же окне браузера в Adobe Reader.
  • Если Adobe Acrobat не установлен, пользователю будет предложено загрузить файл.

Но я хочу, чтобы он всегда появлялся у пользователя для загрузки, независимо от того, установлен Adobe acrobat или нет.

Подскажите, пожалуйста, как я могу это сделать?


person Prashant    schedule 13.12.2008    source источник


Ответы (14)


Вместо ссылки на файл .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); 

P.S.

person TravisO    schedule 13.12.2008
comment
Действительно ли имеет значение, использую ли я следующий код $ file = $ _GET [file] ..pdf; AS $ file = $ _REQUEST [файл] ..pdf; - person Prashant; 13.12.2008
comment
Я столкнулся с другой проблемой: мой файл находится в /products/brochure/myfile.pdf. Я даю переменную $ file как $ file_path = $ _SERVER ['DOCUMENT_ROOT']. '/ Products / brochure /'. $ файл; но он загружает файл как% 2Fvar% 2Fwww% 2Fweb15% 2Fweb% 2Fproducts% 2Fbrochure% 2myfile.pdf - person Prashant; 13.12.2008
comment
что я могу сделать, чтобы он загружался только с именем myfile.pdf. Я изменил заголовок (Content-Disposition: attachment; filename =. Urlencode ('myfile.pdf')); также, но все еще не работает. - person Prashant; 13.12.2008
comment
Я единственный получаю сообщение об ошибке, что .pdf был поврежден, когда я пытаюсь открыть .pdf? - person Tommz; 05.04.2013
comment
@TravisO "Content-type: application/force-download" здесь нигде нет: iana.org/assignments/media-types/ application Это полностью поддельный заголовок. Пожалуйста, не придумывайте заголовки и не отправляйте их. Не могли бы вы обновить свой ответ. Спасибо. - person Nicholas Wilson; 28.08.2013
comment
После поиска в Google и двойной проверки он был удален - person TravisO; 29.08.2013
comment
@TravisO Я получил ошибку, это либо неподдерживаемый тип файла, либо файл был поврежден ... - person Edwin Thomas; 01.10.2014
comment
Однако будьте осторожны при дословном использовании этого кода. Это представляет серьезную уязвимость LFI, поскольку вы передаете GET-переменные непосредственно в fopen. - person Joost; 25.02.2015
comment
Этот код, вероятно, опасен и по-другому. Если вы передадите HTTP-ссылки на fopen, я думаю, он получит файл с другого сервера. Может использоваться злоумышленником для попытки сканирования вашей внутренней сети на наличие открытых файлов PDF. - person Rory McCune; 04.09.2015
comment
Не говоря уже о том, насколько легко было бы обойти любые проверки работоспособности, которые, как вы думаете, вы будете делать с параметром файла. Атаки внедрения пути / обхода каталогов весьма вероятны. - person AviD; 04.09.2015
comment
Если бы я собирался использовать этот код, вместо проверки работоспособности у меня была бы база данных файлов из белого списка на моем сервере, которые можно было бы использовать с ним. Это то, что делают различные CMS. Что касается комментария @RоryMcCune, я думаю, это зависит от того, как настроен ваш PHP. Imo, php.ini должен быть настроен так, чтобы fopen не открывал вещи с удаленных серверов. - person Evan Donovan; 29.11.2016
comment
Вы также можете добавить в свой код что-то, что будет проверять дополнительный параметр $ _GET, который будет действовать как секретный ключ - таким образом люди не смогут создавать произвольные URL-адреса, поскольку они не будут знать ключ, который вы использовали, чтобы разрешить файлу быть обслуженным. Это было бы не совсем безопасно, но было бы лучше. - person Evan Donovan; 29.11.2016
comment
Я думаю, что этот ответ недействителен сегодня, когда у нас есть лучшая альтернатива атрибута HTML5 download - person divy3993; 30.06.2020

Это обычная проблема, но мало кто знает, что есть простое решение 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

person T_D    schedule 09.06.2013
comment
Это простое решение, но, к сожалению, не очень широко поддерживается, особенно. IE не поддерживает caniuse.com/#feat=download - person benebun; 13.11.2013
comment
Ага, я правильно знаю. Вот почему у меня есть примечание о совместимости. И, согласно вашему источнику, IE и Safari не поддерживают этот подход или, по крайней мере, пока нет :) В любом случае, если вы хотите, чтобы все браузеры принудительно загружали, я предлагаю вместо этого проверить некоторые другие ответы ... - person T_D; 14.11.2013
comment
работает как шарм с Chrome Версия 39.0.2171.65 (64-бит)! - person edelans; 21.11.2014
comment
Решение простое, но, к сожалению, не поддерживается в IE и Safari. - person valkirilov; 14.07.2016
comment
говорит, что нет разрешения на доступ - person Hackbal Teamz; 07.08.2018

Не повторяйте каждую строку файла. Вместо этого используйте 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;
}

Обязательно очистите вашу переменную get, так как кто-то может загрузить некоторые файлы php ...

person Alex V    schedule 14.11.2011
comment
Функция readfile действительно работает быстрее. Я лично рекомендую использовать этот ответ вместо принятого - person Jose Garrido; 05.02.2013

Вместо использования PHP-скрипта для чтения и очистки файла удобнее переписать заголовок с использованием .htaccess. Это сохранит "красивый" URL (myfile.pdf вместо download.php?myfile).

<FilesMatch "\.pdf$">
ForceType applicaton/octet-stream
Header set Content-Disposition attachment
</FilesMatch>
person Rob W    schedule 09.02.2012
comment
Разве это не заставит ВСЕ ваши PDF-файлы принудительно загрузить? - person TecBrat; 26.09.2014
comment
@TecBrat Да, но именно об этом спросил ОП. Если вы хотите ограничиться только несколькими PDF-файлами, отредактируйте регулярное выражение ^\.pdf$. - person Rob W; 26.09.2014
comment
@TecBrat или поместите файл .htaccess только в подпапку, где это поведение необходимо. - person habakuk; 05.03.2015

Я нашел способ сделать это с помощью простого старого 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(), чтобы открыть новое окно с файлом.

person Alex W    schedule 06.08.2013

Это ключ:

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

Content-type application / x-pdf-document или application / pdf отправляется при отправке файла PDF. Adobe Reader обычно устанавливает обработчик для этого типа MIME, чтобы браузер передавал документ в Adobe Reader при получении любого из типов PDF MIME.

person Sudden Def    schedule 13.12.2008

Я знаю, что очень поздно отвечу на этот вопрос, но я нашел способ сделать это в javascript.

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

Этого можно добиться с помощью HTML.

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

Добавьте к нему атрибут загрузки: здесь имя файла будет myfile.pdf

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

Укажите значение атрибута загрузки:

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

Здесь имя файла будет Brochure.pdf.

person Okiemute Gold    schedule 19.11.2020
comment
Извините, не работает, он заменит текущий URL-адрес браузера на URL-адрес pdf - person Aljohn Yamaro; 21.05.2021

Попробуй это:

<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);
person Saill    schedule 21.06.2012

Вот другой подход. Я предпочитаю не полагаться на поддержку браузера или решать эту проблему на уровне приложения, а использовать логику веб-сервера.

Если вы используете Apache и можете поместить файл .htaccess в соответствующий каталог, вы можете использовать приведенный ниже код. Конечно, вы также можете поместить это в httpd.conf, если у вас есть к нему доступ.

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

Директива FilesMatch - это просто регулярное выражение, поэтому ее можно настроить так детально, как вы хотите, или вы можете добавить другие расширения.

Строка заголовка выполняет то же самое, что и первая строка в сценариях PHP выше. Если вам нужно также установить строки Content-Type, вы можете сделать это таким же образом, но я не счел это необходимым.

person Evan Donovan    schedule 29.11.2016

Я решил свой, используя весь URL-адрес файла PDF (вместо того, чтобы просто указать имя файла или местоположение в href): a href = "domain. Com / pdf / filename.pdf"

person Mark Allena    schedule 05.06.2017

если вам нужно ограничить скорость загрузки, используйте этот код !!

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

?>

Для получения дополнительной информации щелкните здесь

person Mehran Hooshangi    schedule 06.02.2019

В приложении Ruby on Rails (особенно с чем-то вроде гема Prawn и плагина 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

person btw    schedule 13.12.2008
comment
Где написать этот код? какой контроллер, я новичок в PHP, объясните, пожалуйста. - person Prashant; 13.12.2008
comment
@Prashant Это не PHP, это Ruby on Rails. - person eloyesp; 22.02.2013
comment
@Prashant: Если вам нужен пример PHP, посмотрите те, которые находятся дальше по ветке, но, пожалуйста, прочтите комментарии о том, как они могут быть небезопасными. - person Evan Donovan; 29.11.2016

person    schedule
comment
Вы должны объяснить, что вы предоставили в своем коде. Даже в краткой форме, если это невозможно в деталях или не требуется в деталях. - person Suraj Kumar; 06.02.2019