Способы вывода изображений через PHP скрипты

Раздел: Графика и медиа -> Работа с изображениями

Показ изображений с помощью PHP

Основной эффективный способ отдать изображение через PHP - это скрипт-обработчик, который устанавливает правильные HTTP-заголовки и отправляет содержимое файла. Такой подход позволяет контролировать доступ, менять изображение на лету и кэшировать результат. Ниже приведён базовый пример.


<?php
// Файл show_image.php
$file = $_GET['img'] ?? '';
$path = __DIR__ . '/images/' . basename($file); // безопасный путь

if (!file_exists($path)) {
    http_response_code(404);
    exit('Изображение не найдено');
}

// Определяем MIME-тип
$mime = mime_content_type($path);
header('Content-Type: ' . $mime);
header('Content-Length: ' . filesize($path));

// Кэширование на 1 час
header('Cache-Control: public, max-age=3600');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 3600) . ' GMT');

// Отправка файла
readfile($path);
  

Show img php (показ изображений php)

Шаги реализации:

  • Извлечение имени файла из параметра запроса с фильтрацией через basename() для предотвращения path traversal.
  • Проверка существования файла и отправка 404 при отсутствии.
  • Установка заголовков Content-Type, Content-Length и кэширования.
  • Вывод содержимого через readfile().

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

  • Некорректный MIME-тип - изображение не отображается. Решение: использовать mime_content_type() или finfo.
  • Большой файл и превышение memory_limit. Решение: заменить readfile() на fpassthru() с открытием файла через fopen.
  • Заголовки отправлены до вывода (например, вывод ошибок). Решение: убедиться, что перед header() нет вывода, или использовать output buffering.

Как отобразить изображение из HTML через PHP скрипт?

Простейший вариант - ссылка в теге img указывает на PHP файл с параметром. Скрипт отдаёт файл без изменений. Пример показан выше. Этот способ подходит для статических изображений, когда требуется минимальный контроль.


<img src="show_image.php?img=photo.jpg" alt="Фото">
  

Php font size (размер шрифта в php)

Как ограничить доступ к изображениям только авторизованным пользователям?

Добавить проверку сессии или токена перед отправкой файла.


<?php
session_start();
if (!isset($_SESSION['user_id'])) {
    http_response_code(403);
    exit('Доступ запрещён');
}
// далее стандартный код показа изображения
  

Php height (получение высоты в php)

Проблема: кэширование может сохранить изображение в браузере и обойти защиту. Решение: добавить заголовок Cache-Control: no-cache, no-store, must-revalidate.

Как вывести на лету уменьшенную копию (thumbnail) средствами GD?

Использовать библиотеку GD для масштабирования перед отправкой.


<?php
$src = 'images/large.jpg';
$width = 200;
$height = 150;

list($w_orig, $h_orig) = getimagesize($src);
$ratio = min($width/$w_orig, $height/$h_orig);
$new_w = (int)($w_orig * $ratio);
$new_h = (int)($h_orig * $ratio);

$thumb = imagecreatetruecolor($new_w, $new_h);
$source = imagecreatefromjpeg($src);
imagecopyresampled($thumb, $source, 0, 0, 0, 0, $new_w, $new_h, $w_orig, $h_orig);

header('Content-Type: image/jpeg');
imagejpeg($thumb, null, 90);
imagedestroy($thumb);
imagedestroy($source);
  

Проблема: каждый запрос создаёт уменьшенную копию, что нагружает процессор. Решение: кэшировать результат в файл или использовать кэширование HTTP. Также возможна ошибка при отсутствии поддержки GD - проверить extension_loaded('gd').

Как уменьшить нагрузку при частых запросах с помощью HTTP-кэширования (ETag, Last-Modified)?

Реализовать проверку If-Modified-Since и If-None-Match.


<?php
$file = 'image.jpg';
$last_modified = filemtime($file);
$etag = md5_file($file);

header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $last_modified) . ' GMT');
header('ETag: "' . $etag . '"');

if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
    $modified = $_SERVER['HTTP_IF_MODIFIED_SINCE'] ?? '';
    $match = $_SERVER['HTTP_IF_NONE_MATCH'] ?? '';
    if ($match === '"' . $etag . '"' || strtotime($modified) >= $last_modified) {
        header('HTTP/1.1 304 Not Modified');
        exit;
    }
}
// далее отправка файла
  

Проблема: неправильное формирование ETag или Last-Modified может привести к нерабочему кэшированию. Решение: использовать md5_file для ETag и filemtime для Last-Modified.

Как отобразить изображение, хранящееся в базе данных (BLOB)?

Извлечь бинарные данные и отдать с правильным MIME-типом.


<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->prepare('SELECT data, mime FROM images WHERE id = ?');
$stmt->execute([$_GET['id']]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) {
    http_response_code(404);
    exit;
}
header('Content-Type: ' . $row['mime']);
header('Content-Length: ' . strlen($row['data']));
echo $row['data'];
  

Проблема: большие BLOB могут сильно нагружать память и базу. Решение: для больших файлов лучше хранить ссылки на файловую систему.

Как ускорить отдачу больших файлов с помощью X-Sendfile (nginx/apache)?

Использовать заголовок X-Sendfile, который перенаправляет задачу отдачи файла самому веб-серверу, минуя PHP.


<?php
$path = '/var/www/images/secret.pdf';
header('X-Sendfile: ' . $path);
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="secret.pdf"');
exit;
  

Проблема: модуль X-Sendfile может быть не включён в конфигурации сервера. Решение: установить nginx_http_sendfile или mod_xsendfile для Apache. Также путь должен быть абсолютным и доступным для сервера.

Расширенные примеры отображения изображений через PHP

Пример 1. Скрипт с поддержкой кэширования, обработкой ошибок и ресайзом с сохранением пропорций

Создаётся единый скрипт, который принимает параметры: имя файла, ширину и высоту. Если параметры масштабирования не указаны, отдаётся оригинал. Результат ресайза кэшируется в отдельную директорию.

Пример

<?php
// img.php
$file = basename($_GET['file'] ?? '');
$src = __DIR__ . '/originals/' . $file;
if (!file_exists($src)) {
    http_response_code(404);
    exit('Not Found');
}

$width = isset($_GET['w']) ? (int)$_GET['w'] : 0;
$height = isset($_GET['h']) ? (int)$_GET['h'] : 0;

if ($width > 0 && $height > 0) {
    $cacheDir = __DIR__ . '/cache/';
    $cacheFile = $cacheDir . md5($file . $width . $height) . '.jpg';
    if (!file_exists($cacheFile)) {
        list($w_orig, $h_orig) = getimagesize($src);
        $ratio = min($width/$w_orig, $height/$h_orig);
        $new_w = (int)($w_orig * $ratio);
        $new_h = (int)($h_orig * $ratio);
        $thumb = imagecreatetruecolor($new_w, $new_h);
        $source = imagecreatefromjpeg($src); // для JPEG
        imagecopyresampled($thumb, $source, 0, 0, 0, 0, $new_w, $new_h, $w_orig, $h_orig);
        imagejpeg($thumb, $cacheFile, 85);
        imagedestroy($thumb);
        imagedestroy($source);
    }
    $file = $cacheFile;
}

$mime = mime_content_type($file);
header('Content-Type: ' . $mime);
header('Content-Length: ' . filesize($file));
header('Cache-Control: public, max-age=86400');
readfile($file);

Результат: при запросе img.php?file=photo.jpg&w=200&h=150 будет создан и отдан кэшированный файл.

Пример 2. Вывод изображения в формате WebP с поддержкой браузеров (GD + кэширование)

Скрипт определяет, поддерживает ли браузер WebP (через заголовок Accept), и если да, конвертирует JPEG/PNG в WebP на лету. Результат кэшируется для повторных запросов.

Пример

<?php
$src = 'image.jpg';
$cacheDir = __DIR__ . '/webp/';
$cacheFile = $cacheDir . md5_file($src) . '.webp';

if (!file_exists($cacheFile)) {
    if (!is_dir($cacheDir)) {
        mkdir($cacheDir, 0755, true);
    }
    $im = imagecreatefromjpeg($src);
    imagewebp($im, $cacheFile, 80);
    imagedestroy($im);
}

$supportWebp = isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false;
if ($supportWebp) {
    header('Content-Type: image/webp');
    header('Content-Length: ' . filesize($cacheFile));
    readfile($cacheFile);
} else {
    header('Content-Type: image/jpeg');
    header('Content-Length: ' . filesize($src));
    readfile($src);
}

Результат для браузера, поддерживающего WebP, - заголовок Content-Type будет image/webp.

Пример 3. Защита от hotlink (прямых ссылок) с проверкой HTTP_REFERER

Пример

<?php
$allowed = ['mysite.com', 'www.mysite.com'];
$ref = parse_url($_SERVER['HTTP_REFERER'] ?? '', PHP_URL_HOST);
if (!in_array($ref, $allowed)) {
    // отдать заглушку
    header('Content-Type: image/gif');
    readfile('no_hotlink.gif');
    exit;
}
// нормальный показ файла

Можно заменить заглушку на изображение с предупреждением.

Пример 4. Использование Imagick для обработки с сохранением в буфер

Пример

<?php
$image = new Imagick('image.png');
$image->thumbnailImage(300, 0); // 300px по ширине, высота пропорционально
$image->setImageFormat('jpg');
$image->setImageCompressionQuality(90);
header('Content-Type: image/jpeg');
echo $image->getImageBlob();
$image->clear();

Imagick обычно быстрее GD и поддерживает больше форматов.

Пример 5. Команда curl для проверки заголовков ответа скрипта

Пример

curl -I http://example.com/show_image.php?img=test.jpg

Результат (пример вывода):

HTTP/1.1 200 OK
Content-Type: image/jpeg
Content-Length: 12345
Cache-Control: public, max-age=3600
…

показ изображений PHP - comments

En
Show img php (php)