Лучший способ кэшировать изображения с измененным размером с помощью PHP и MySQL
Каков наилучший практический способ обработки кэширования изображений с использованием PHP.
Имя файла в настоящее время хранится в базе данных MySQL, которая при загрузке переименовывается в идентификатор GUID вместе с исходным именем файла и тегом alt.
Когда изображение помещается на HTML-страницы, это делается с помощью URL-адреса, такого как '/images/get/200x200/{guid}.jpg, который переписывается в php-скрипт. Это позволяет моим дизайнерам указывать (примерно - исходное изображение может быть меньше) размер файла.
Затем php-скрипт создает хэш размером (200x200 в URL-адресе) и имя файла GUID, и если файл был сгенерирован ранее (файл с именем хэша существует в каталоге TMP), отправляет файл из каталога TMP приложения. Если хэшированное имя файла не существует, то оно создается, записывается на диск и подается таким же образом,
Действительно ли это эффективно, как могло бы быть? (Он также поддерживает водяные знаки, изображения и настройки водяных знаков хранятся в хэш также, но это выходит за рамки этого.)
9 answers
В примере переписывания Дэна Удея есть две опечатки (и я не могу это прокомментировать), скорее это должно быть:
RewriteCond %{REQUEST_URI} ^/images/cached/
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
RewriteRule (.*) /images/generate.php?$1 [L]
С уважением.
Я бы сделал это по-другому.
Проблемы: 1. Использование PHP для передачи файлов менее эффективно, чем могло бы быть. 2. PHP должен проверять наличие файлов каждый раз, когда запрашивается изображение 3. Apache намного лучше в этом, чем PHP когда-либо будет.
Здесь есть несколько решений.
Вы можете использовать mod_rewrite
на Apache. Можно использовать mod_rewrite, чтобы проверить, существует ли файл, и если да, то вместо этого используйте этот файл. Это полностью обходит PHP и делает все происходит гораздо быстрее. Реальный способ сделать это, однако, состоял бы в том, чтобы сгенерировать определенную схему URL, которая всегда должна существовать, а затем перенаправить на PHP, если нет.
Например:
RewriteCond %{REQUEST_URI} ^/images/cached/
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
RewriteRule (.*) /images/generate.php?$1 [L]
Поэтому, если клиент запрашивает /images/cached/<something>
, а этот файл еще не существует, Apache перенаправит запрос на /images/generate.php?/images/cached/<something>
. Этот скрипт может затем сгенерировать изображение, записать его в кэш, а затем отправить клиенту. В будущем PHP-скрипт никогда не вызывается, за исключением новых изображений.
Используйте кэширование. Как другой плакат гласил: используйте такие вещи, как mod_expires
, Последние измененные заголовки и т. Д. для ответа на условные запросы GET. Если клиенту не придется повторно запрашивать изображения, загрузка страниц значительно ускорится, а нагрузка на сервер уменьшится.
В случаях, когда вам действительно нужно отправить изображение с PHP, вы можете использовать mod_xsendfile
, чтобы сделать это с меньшими затратами. Смотрите отличное сообщение в блоге Арнольда Дэниелса по этому вопросу, но обратите внимание, что его пример предназначен для загрузки. Чтобы подавать изображения встраиваемыми, выньте заголовок Размещения содержимого (третий вызов заголовка()).
Надеюсь, это поможет - больше после того, как моя мигрень пройдет.
Одно замечание, которое стоит добавить, - это убедиться, что ваш код не генерирует "несанкционированные" размеры этих изображений.
Таким образом, следующий URL-адрес создаст версию изображения 1234 размером 200x200, если она еще не существует. Я бы настоятельно рекомендовал вам убедиться, что запрошенный URL-адрес содержит поддерживаемые вами размеры изображения.
/images/get/200x200/1234.jpg
Злоумышленник может начать запрашивать случайные URL-адреса, всегда изменяя высоту и ширину изображения. Это может привести к серьезным последствиям для вашего сервера проблемы b/c он будет сидеть там, по сути, под атакой, генерируя изображения размеров, которые вы не поддерживаете.
/images/get/0x1/1234.jpg
/images/get/0x2/1234.jpg
...
/images/get/0x9999999/1234.jpg
/images/get/1x1/1234.jpg
...
etc
Вот случайный фрагмент кода, иллюстрирующий это:
<?php
$pathOnDisk = getImageDiskPath($_SERVER['REQUEST_URI']);
if(file_exists($pathOnDisk)) {
// send header with image mime type
echo file_get_contents($pathOnDisk);
exit;
} else {
$matches = array();
$ok = preg_match(
'/\/images\/get\/(\d+)x(\d+)\/(\w+)\.jpg/',
$_SERVER['REQUEST_URI'], $matches);
if(! $ok) {
// invalid url
handleInvalidRequest();
} else {
list(, $width, $height, $guid) = $matches;
// you should do this!
if(isSupportedSize($width, $height)) {
// size is supported. all good
// generate the resized image, save it & output it
} else {
// invalid size requested!!!
handleInvalidRequest();
}
}
}
// snip
function handleInvalidRequest() {
// do something w/ invalid request
// show a default graphic, log it etc
}
?>
Кажется отличным постом, но моя проблема все еще остается нерешенной. У меня нет доступа к htaccess у моего хост-провайдера, так что о настройке apache не может быть и речи. Есть ли на самом деле способ установить заголовок cace-control для изображений?
Ваш подход кажется вполне разумным - я бы добавил, что следует внедрить некоторый механизм, чтобы проверить, что дата создания кэшированной версии была после последней измененной метки времени исходного (исходного) файла изображения, и если не восстановить кэшированную/измененную версию. Это гарантирует, что в случае изменения изображения дизайнерами кэш будет соответствующим образом обновлен.
Это звучит как надежный способ сделать это. Следующим шагом может быть выход за рамки PHP/MySQL.
Возможно, измените свои заголовки:
Если вы используете PHP для отправки типов MIME, вы также можете использовать заголовки 'Keep-alive' и 'Cache-control', чтобы продлить срок службы ваших изображений на сервере и снять часть нагрузки с PHP/MySQL.
Кроме того, рассмотрите также плагины apache для кэширования. Как mod_экспирирует.
О, еще одна вещь, насколько ты контролируешь есть над вашим сервером? Должны ли мы ограничить этот разговор только PHP/MySQL?
Мне удалось сделать это просто с помощью заголовка перенаправления в PHP:
if (!file_exists($filename)) {
// *** Insert code that generates image ***
// Content type
header('Content-type: image/jpeg');
// Output
readfile($filename);
} else {
// Redirect
$host = $_SERVER['HTTP_HOST'];
$uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\\');
$extra = $filename;
header("Location: http://$host$uri/$extra");
}
Вместо сохранения адреса файла в бд я предпочитаю добавлять случайное число к имени файла всякий раз, когда пользователь входит в систему. Что-то вроде этого для пользователя 1234: изображение/picture_1234.png?rnd=6534122341
Если пользователь отправляет новое изображение во время сеанса, я просто обновляю случайное число.
Идентификатор GUID решает проблему кэша на 100%. Однако это в некотором роде затрудняет отслеживание файлов изображений. С помощью этого метода есть вероятность, что пользователь может увидеть ту же картинку снова при следующем входе в систему. Однако шансы невелики, если вы сгенерируете свое случайное число из миллиарда чисел.
PhpThumb - это фреймворк, который генерирует изображения/миниатюры с измененным размером на лету. Он также реализует кэширование, и его очень легко реализовать.
Код для изменения размера изображения:
<img src="/phpThumb.php?src=/path/to/image.jpg&w=200&h=200" alt="thumbnail"/>
Даст вам миниатюру размером 200 х 200;
Он также поддерживает водяные знаки.
Проверьте это на: http://phpthumb.sourceforge.net/