Способы вывода изображений через 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 …