Как вернуть файл в PHP: основные методы и их применение

Раздел: Основы PHP -> Основы PHP

Основные подходы к возврату файлов

Как эффективно отдать файл браузеру для скачивания?

Самый производительный способ отдать файл клиенту — использовать функцию readfile() в сочетании с правильными HTTP-заголовками. Этот метод не загружает содержимое файла в память целиком, а передаёт его напрямую из файловой системы в выходной буфер. Подходит для файлов любого размера.


function downloadFile(string $filePath, string $fileName = null): bool {
    if (!file_exists($filePath)) {
        return false;
    }
    if ($fileName === null) {
        $fileName = basename($filePath);
    }
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . $fileName . '"');
    header('Content-Length: ' . filesize($filePath));
    readfile($filePath);
    exit;
}

Php форматы данных (форматы данных в php (json, xml, serialize))

После вызова readfile() управление не возвращается, поэтому функция завершает скрипт через exit. Это предотвращает вывод лишнего содержимого после файла.

Типичные ошибки:

  • Ошибка 404 при неверном пути — проверять file_exists().
  • Заголовки не отправляются, если до вывода есть пробелы или echo. Использовать ob_clean() перед заголовками.
  • Большой файл может превысить время выполнения — увеличить max_execution_time.

Как получить содержимое файла строкой для обработки?

Если нужно не отдать файл, а использовать его данные в скрипте, подходит file_get_contents(). Функция возвращает строку с содержимым файла.


function getFileContent(string $path): ?string {
    if (!is_readable($path)) {
        return null;
    }
    return file_get_contents($path);
}

Php null false (null и false в php)

Используется для чтения конфигураций, шаблонов, небольших файлов данных.

Проблемы:

  • При больших файлах (более десятков мегабайт) может произойти переполнение памяти. Лучше применять потоковые методы.
  • Ошибка возвращает false, поэтому нужно проверять строгим оператором ===.

Как вернуть большой файл частями, не загружая его в память?

Для чтения огромных файлов (например, видео или дампов) используют fopen() + fread() в цикле. Каждый фрагмент данных отправляется сразу в вывод.


function streamFileChunks(string $path, int $chunkSize = 8192): bool {
    $handle = fopen($path, 'rb');
    if (!$handle) {
        return false;
    }
    header('Content-Type: application/octet-stream');
    while (!feof($handle)) {
        $chunk = fread($handle, $chunkSize);
        if ($chunk === false) {
            break;
        }
        echo $chunk;
        flush();
    }
    fclose($handle);
    return true;
}

Php get started (начало работы с php)

Этот вариант полезен, когда readfile() недоступен или требуется дополнительная логика перед каждым фрагментом.

Ошибки:

  • Не закрыт файловый дескриптор — использовать fclose() в конце.
  • Чтение до конца файла может генерировать warnings, если файл повреждён. Проверять возвращаемое значение fread().

Как отправить уже открытый поток напрямую клиенту?

Функция fpassthru() выводит оставшуюся часть открытого файлового потока, начиная с текущей позиции.


function outputStream($handle): void {
    fpassthru($handle);
    fclose($handle);
}

Custom index php (создание собственного index.php)

Применяется, когда файл уже открыт для чтения (например, после работы с ZIP-архивом).

Особенность: если поток уже был частично прочитан, будут отправлены только оставшиеся байты. Нужно убедиться, что текущая позиция в начале файла, или перемотать rewind().

Как скопировать поток с минимальным потреблением памяти?

stream_copy_to_stream() копирует данные из одного потока в другой, например из файла в php://output.


function copyFileToOutput(string $path): bool {
    $in = fopen($path, 'rb');
    if (!$in) {
        return false;
    }
    $out = fopen('php://output', 'wb');
    if (!$out) {
        fclose($in);
        return false;
    }
    stream_copy_to_stream($in, $out);
    fclose($in);
    fclose($out);
    return true;
}

Метод полезен при работе с удалёнными файлами после fopen() с обёртками (например, ftp://).

Ошибки: не забудьте закрыть оба потока. При неудаче fopen() возвращает false — необходима проверка.

- Default php file (файл по умолчанию в php)
- Php return file (возврат файла из функции в php)
- File php query (запрос к файлу в php)

Расширенные примеры возврата файлов

Функция для скачивания с определением MIME-типа

Используется mime_content_type() для автоматического определения типа файла.

Пример

function downloadWithMime(string $file): void {
    if (!file_exists($file)) {
        http_response_code(404);
        exit('Файл не найден');
    }
    $mime = mime_content_type($file);
    $name = basename($file);
    header('Content-Type: ' . $mime);
    header('Content-Disposition: attachment; filename="' . $name . '"');
    header('Content-Length: ' . filesize($file));
    readfile($file);
    exit;
}
// Вызов: downloadWithMime('document.pdf')
// Браузер получит заголовок Content-Type: application/pdf

Поддержка частичной загрузки (Range headers)

Для возобновления скачивания и видео-потоков обрабатывают заголовок HTTP_RANGE.

Пример

function rangeDownload(string $file): void {
    $size = filesize($file);
    $fp = fopen($file, 'rb');
    $start = 0;
    $end = $size - 1;
    header('Accept-Ranges: bytes');
    if (isset($_SERVER['HTTP_RANGE'])) {
        preg_match('/bytes=(\d+)-(\d*)/', $_SERVER['HTTP_RANGE'], $matches);
        $start = intval($matches[1]);
        if (!empty($matches[2])) {
            $end = intval($matches[2]);
        }
        header('HTTP/1.1 206 Partial Content');
        header("Content-Range: bytes $start-$end/$size");
    }
    header('Content-Length: ' . ($end - $start + 1));
    header('Content-Type: application/octet-stream');
    fseek($fp, $start);
    $chunk = 8192;
    while ($start <= $end && !feof($fp)) {
        $readLen = min($chunk, $end - $start + 1);
        $data = fread($fp, $readLen);
        if ($data === false) break;
        echo $data;
        $start += strlen($data);
        flush();
    }
    fclose($fp);
}
// При запросе с заголовком Range: bytes=0-1023 вернёт первые 1024 байта.

Возврат файла с использованием output buffering

Иногда необходимо очистить буфер перед отправкой двоичных данных.

Пример

function cleanEchoReadfile(string $path): void {
    ob_clean();          // очищаем предыдущий вывод
    flush();
    readfile($path);
    exit;
}
// Функция гарантирует, что никакой текст до вызова не повлияет на скачивание.

Возврат сжатого файла (Gzip)

Если клиент поддерживает gzip, можно сжать файл на лету.

Пример

function gzipReturn(string $file): void {
    if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false) {
        header('Content-Encoding: gzip');
        echo gzencode(file_get_contents($file));
    } else {
        readfile($file);
    }
}
// Для текстовых файлов размер может уменьшиться в 3-5 раз.

Возврат файла с проверкой на пустой вывод

Если файл пустой, можно вернуть сообщение об ошибке вместо скачивания пустого файла.

Пример

function safeReadfile(string $path): void {
    if (!file_exists($path) || filesize($path) == 0) {
        http_response_code(204);
        exit('No content');
    }
    readfile($path);
}
// Для пустого файла клиент получает HTTP 204, что интерпретируется как отсутствие данных.

Возврат файла из функции в PHP - comments

En
Php return file (php)