Скачивание файлов в PHP: от простого к сложному
Скачать страницу PHP: варианты принудительной выдачи файла
Как сделать, чтобы PHP отдавал результат скрипта как файл для скачивания?
Основной способ: отправка заголовков и чтение файла
Наиболее эффективное решение использует функцию header() для установки MIME-типа и Content-Disposition, а затем readfile() для вывода содержимого. Этот подход работает с любыми файлами, хранящимися на сервере.
// Файл download.php
$file = 'path/to/document.pdf';
$filename = basename($file);
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
exit;
} else {
echo 'Файл не найден.';
}
Include system php (включение системного файла)
Пояснение: Content-Disposition: attachment заставляет браузер открыть диалог сохранения. filesize() добавляет правильную длину контента. readfile() выводит содержимое порциями, экономя память.
Типичная ошибка:
Вывод пробелов или предупреждений до вызова header() приводит к ошибке headers already sent. Решение: убедиться, что перед header() нет вывода (echo, HTML, пробелы до <?php). Использовать ob_start() для буферизации вывода.
Как сохранить результат выполнения PHP-скрипта в файл на сервере?
Используется комбинация ob_start() и file_put_contents() для захвата всего вывода скрипта.
ob_start(); // начало буферизации вывода
echo '<html><body>...</body></html>';
$content = ob_get_clean(); // получение и очистка буфера
file_put_contents('cached_page.html', $content);
Ru reading php line (чтение строки из файла php)
Такой вариант полезен для кэширования динамических страниц или генерации статических HTML-копий.
Ошибка: если вызывать ob_get_clean() до закрытия буфера, результат может быть пустым. Проверить вложенность буферов.
Как скачать удалённую страницу через PHP?
Для получения содержимого внешнего URL применяется file_get_contents() или cURL. Затем результат можно отдать пользователю.
$url = 'https://example.com/page.php';
$content = file_get_contents($url);
header('Content-Type: text/html');
header('Content-Disposition: attachment; filename="page.html"');
echo $content;
Php file get html (получение html файла через php)
При больших объёмах данных лучше использовать cURL с потоковой записью, чтобы не перегружать память.
Проблема: file_get_contents() может быть заблокирован на сервере (allow_url_fopen = Off). В таком случае применяется cURL.
Как обеспечить скачивание файла через прямую ссылку с атрибутом download?
В HTML5 можно просто добавить атрибут download к ссылке, указывающей на PHP-скрипт, который выводит файл. Это не требует отдельного PHP-обработчика.
<a href="download.php" download>Скачать файл</a>
Скрипт download.php должен выводить нужные заголовки.
Расширенные примеры скачивания файлов через PHP
Пример 1. Скачивание динамически созданного CSV-файла
// generate_csv.php
$data = [
['Имя', 'Email', 'Дата'],
['Иван', 'ivan@example.com', '2024-01-15'],
['Мария', 'maria@example.com', '2024-02-20']
];
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="contacts.csv"');
$output = fopen('php://output', 'w');
fprintf($output, chr(0xEF).chr(0xBB).chr(0xBF)); // BOM для Excel
foreach ($data as $row) {
fputcsv($output, $row);
}
fclose($output);
Результат: браузер скачивает файл contacts.csv с корректной кодировкой UTF-8.
Пример 2. Скачивание файла с проверкой прав доступа
// secure_download.php
session_start();
if (!isset($_SESSION['user'])) {
header('HTTP/1.0 403 Forbidden');
echo 'Доступ запрещён';
exit;
}
$file = '/var/www/private/data.txt';
if (!file_exists($file) || !is_readable($file)) {
header('HTTP/1.0 404 Not Found');
exit;
}
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="data.txt"');
header('Content-Length: ' . filesize($file));
readfile($file);
Результат: файл скачивается только для авторизованных пользователей.
Пример 3. Скачивание большого файла с прогрессом через X-Sendfile (Apache/Nginx)
// xsendfile.php
$file = '/var/www/large_video.mp4';
header('X-Sendfile: ' . $file);
header('Content-Type: video/mp4');
header('Content-Disposition: attachment; filename="video.mp4"');
exit;
Результат: веб-сервер отдаёт файл напрямую, не нагружая PHP. Требуется модуль mod_xsendfile.
Пример 4. Скачивание архива из нескольких файлов на лету
// zip_download.php
$zip = new ZipArchive();
$tmp = tempnam(sys_get_temp_dir(), 'zip');
if ($zip->open($tmp, ZipArchive::CREATE) !== TRUE) {
exit('Ошибка создания архива');
}
$zip->addFile('doc1.pdf', 'doc1.pdf');
$zip->addFile('doc2.pdf', 'doc2.pdf');
$zip->close();
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="documents.zip"');
header('Content-Length: ' . filesize($tmp));
readfile($tmp);
unlink($tmp); // удаляем временный файл
Результат: скачивается ZIP-архив, созданный динамически.
Пример 5. Скачивание с возобновлением (Range запросы)
// resume_download.php
$file = 'largefile.iso';
$fp = fopen($file, 'rb');
$size = filesize($file);
$start = 0;
$end = $size - 1;
if (isset($_SERVER['HTTP_RANGE'])) {
preg_match('/bytes=(\d+)-(\d*)/', $_SERVER['HTTP_RANGE'], $matches);
$start = intval($matches[1]);
$end = ($matches[2] === '') ? $size - 1 : intval($matches[2]);
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size);
} else {
header('HTTP/1.1 200 OK');
}
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="largefile.iso"');
header('Content-Length: ' . ($end - $start + 1));
fseek($fp, $start);
$chunkSize = 1024 * 1024; // 1 MB
while (!feof($fp) && ftell($fp) <= $end) {
$remaining = $end - ftell($fp) + 1;
if ($remaining < $chunkSize) $chunkSize = $remaining;
echo fread($fp, $chunkSize);
flush();
}
fclose($fp);
Результат: поддерживается докачка больших файлов.