Работа с изображениями в PHP: все способы вывода и обработки
Основные методы работы с изображениями в PHP
Наиболее эффективное решение для вывода изображений через PHP - это скрипт, который читает файл и отправляет его с корректными HTTP-заголовками. Такой подход позволяет скрыть реальный путь к файлу, проверять права доступа, динамически генерировать или изменять изображение.
// image.php
$filename = __DIR__ . '/uploads/' . basename($_GET['file']);
if (!file_exists($filename)) {
http_response_code(404);
exit('Изображение не найдено');
}
$mime = mime_content_type($filename);
header('Content-Type: ' . $mime);
header('Content-Length: ' . filesize($filename));
header('Cache-Control: public, max-age=86400');
readfile($filename);
Page img php (изображение на php)
В этом примере скрипт принимает имя файла через GET-параметр, проверяет его существование, устанавливает правильный MIME-тип и размер, включает кеширование и выводит содержимое. Использование basename предотвращает directory traversal.
Типичная ошибка: если после readfile или до заголовков происходит вывод любого текста (включая пробелы), то заголовки не отправляются. Решение - убедиться, что перед header() нет вывода, использовать ob_start() и ob_end_clean() при необходимости.
Другая проблема: изображение не отображается, если MIME-тип определён неверно. Проверяйте mime_content_type или задавайте явный тип по расширению.
Цель и случаи использования: защита прямых ссылок, контроль доступа, логирование скачиваний, динамическая обработка (например, изменение размера).
Как вывести изображение из базы данных?
Изображения можно хранить в BLOB-поле MySQL. При запросе PHP читает данные из БД и выводит их с заголовком Content-Type, полученным из другого поля или заданным вручную.
// Пример для MySQLi
$stmt = $db->prepare('SELECT mime, data FROM images WHERE id = ?');
$stmt->bind_param('i', $id);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($mime, $data);
$stmt->fetch();
header('Content-Type: ' . $mime);
echo $data;
Проблема: изображение может быть слишком большим для хранения в БД и медленно выводиться. Решение - хранить в БД только метаданные, а файлы - на диске.
Ошибка: не указан MIME-тип - браузер может не распознать формат. Храните mime вместе с данными.
Цель: когда нужна тесная интеграция изображений с другими данными в БД, например, в системах управления контентом.
Как изменить размер изображения на лету?
Библиотека GD или Imagick позволяет ресайзить изображение перед выводом. Пример с GD:
$src = imagecreatefromjpeg('photo.jpg');
$width = imagesx($src);
$height = imagesy($src);
$new_w = 200;
$new_h = intval($height * $new_w / $width);
$thumb = imagecreatetruecolor($new_w, $new_h);
imagecopyresampled($thumb, $src, 0,0,0,0, $new_w, $new_h, $width, $height);
header('Content-Type: image/jpeg');
imagejpeg($thumb, null, 90);
imagedestroy($src);
imagedestroy($thumb);
Ошибка: если параметры ширины и высоты не пропорциональны, изображение искажается. Сохраняйте пропорции, как в примере.
Проблема: при частых запросах создание миниатюр нагружает сервер. Используйте кеширование сгенерированных файлов.
Цель: отображение миниатюр, адаптивных вариантов для разных устройств.
Как защитить изображения от прямого доступа?
Скрипт вывода проверяет сессию, куки или права пользователя перед чтением файла.
session_start();
if (!isset($_SESSION['user_id'])) {
http_response_code(403);
exit('Доступ запрещён');
}
$filename = '/private/'.$fileName;
header('Content-Type: image/jpeg');
readfile($filename);
Ошибка: если файл физически доступен по URL (например, в public_html), защита не сработает. Храните файлы вне document root.
Проблема: утечка пути через ошибки. Используйте абсолютные пути и проверку.
Цель: платный контент, изображения только для авторизованных пользователей.
Как создать изображение с текстом?
GD позволяет наносить текст на изображение. Полезно для создания капчи, сертификатов, водяных знаков.
$im = imagecreatetruecolor(400, 100);
$bg = imagecolorallocate($im, 255, 255, 255);
$textcolor = imagecolorallocate($im, 0, 0, 0);
imagefilledrectangle($im, 0, 0, 400, 100, $bg);
$font = 'arial.ttf';
imagettftext($im, 20, 0, 50, 60, $textcolor, $font, 'Привет, мир!');
header('Content-Type: image/png');
imagepng($im);
imagedestroy($im);
Ошибка: если шрифт не найден, функция вернёт false. Укажите абсолютный путь к файлу шрифта.
Проблема: текст выходит за границы изображения. Рассчитывайте положение заранее.
Цель: динамические баннеры, капча, изображения с переменными данными.
Расширенные примеры работы с изображениями в PHP
1. Скрипт вывода с кешированием (ETag и Last-Modified)
Этот скрипт позволяет клиенту кешировать изображение и не загружать его повторно, если оно не изменилось.
$filename = __DIR__ . '/images/picture.jpg';
if (!file_exists($filename)) {
http_response_code(404);
exit;
}
$lastModified = filemtime($filename);
$etag = md5_file($filename);
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModified) . ' GMT');
header('ETag: "' . $etag . '"');
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$ifMod = $_SERVER['HTTP_IF_MODIFIED_SINCE'] ?? '';
$ifNone = $_SERVER['HTTP_IF_NONE_MATCH'] ?? '';
if (strtotime($ifMod) >= $lastModified || $ifNone === '"'.$etag.'"') {
http_response_code(304);
exit;
}
}
$mime = mime_content_type($filename);
header('Content-Type: ' . $mime);
header('Content-Length: ' . filesize($filename));
readfile($filename);
Браузер получит код 304 Not Modified и использует закешированную версию.
2. Обработка загрузки изображения с созданием миниатюры
Загрузка файла на сервер с проверкой типа, размера и последующей генерацией миниатюры.
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) {
$file = $_FILES['image'];
$allowed = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($file['type'], $allowed)) {
die('Недопустимый тип файла');
}
if ($file['size'] > 5 * 1024 * 1024) {
die('Файл слишком большой');
}
$uploadPath = __DIR__ . '/uploads/' . basename($file['name']);
move_uploaded_file($file['tmp_name'], $uploadPath);
// Создание миниатюры 150x150
$thumb = imagecreatetruecolor(150, 150);
$src = imagecreatefromjpeg($uploadPath);
imagecopyresampled($thumb, $src, 0,0,0,0, 150, 150, imagesx($src), imagesy($src));
$thumbPath = __DIR__ . '/thumbs/thumb_' . $file['name'];
imagejpeg($thumb, $thumbPath, 80);
imagedestroy($src);
imagedestroy($thumb);
echo 'Изображение загружено и миниатюра создана';
}
На сервере появятся оригинал в uploads/ и миниатюра в thumbs/
3. Наложение водяного знака с прозрачностью
Добавление полупрозрачного логотипа в нижний правый угол изображения.
$src = imagecreatefromjpeg('photo.jpg');
$watermark = imagecreatefrompng('logo.png');
$srcW = imagesx($src);
$srcH = imagesy($src);
$wmW = imagesx($watermark);
$wmH = imagesy($watermark);
$dstX = $srcW - $wmW - 10;
$dstY = $srcH - $wmH - 10;
imagecopy($src, $watermark, $dstX, $dstY, 0, 0, $wmW, $wmH);
header('Content-Type: image/jpeg');
imagejpeg($src, null, 90);
imagedestroy($src);
imagedestroy($watermark);
На выходном изображении в правом нижнем углу отобразится логотип.
4. Генерация изображения-заглушки (placeholder) с текстом
Создание простого изображения с заданными размерами и текстом, например, для тестовых макетов.
function createPlaceholder($width, $height, $text) {
$im = imagecreatetruecolor($width, $height);
$bg = imagecolorallocate($im, 200, 200, 200);
$textColor = imagecolorallocate($im, 50, 50, 50);
imagefilledrectangle($im, 0, 0, $width, $height, $bg);
$fontSize = max(12, min($width, $height) / 10);
$fontFile = __DIR__ . '/fonts/arial.ttf';
$bbox = imagettfbbox($fontSize, 0, $fontFile, $text);
$textWidth = $bbox[2] - $bbox[0];
$textHeight = $bbox[1] - $bbox[7];
$x = ($width - $textWidth) / 2;
$y = ($height + $textHeight) / 2;
imagettftext($im, $fontSize, 0, $x, $y, $textColor, $fontFile, $text);
header('Content-Type: image/png');
imagepng($im);
imagedestroy($im);
}
// Пример вызова
createPlaceholder(400, 300, '300 x 400');
Браузер отобразит серый прямоугольник с текстом "300 x 400" по центру.