Лучший способ кэшировать изображения с измененным размером, используя PHP и MySQL - PullRequest
14 голосов
/ 26 сентября 2008

Каков наилучший практический способ обработки кэширования изображений с использованием PHP.

Имя файла в настоящее время хранится в базе данных MySQL, которая при загрузке переименовывается в GUID вместе с исходным именем файла и тегом alt.

Когда изображение помещается на HTML-страницы, это делается с помощью URL-адреса, например '/images/get/200x200/ оформленияguid‹.jpg, который переписывается в php-скрипт. Это позволяет моим дизайнерам указать (примерно - исходное изображение может быть меньше) размер файла.

Затем скрипт php создает хэш размера (200x200 в URL) и имени файла GUID, и, если файл был сгенерирован ранее (файл с именем хэша существует в каталоге TMP), отправляет файл из приложения. Каталог TMP. Если хешированное имя файла не существует, оно создается, записывается на диск и обрабатывается таким же образом,

Это эффективно, как могло бы быть? (Он также поддерживает водяные знаки для изображений, и настройки водяных знаков также сохраняются в хэше, но это выходит за рамки этого.)

Ответы [ 9 ]

30 голосов
/ 26 сентября 2008

Я бы сделал это по-другому.

Проблемы: 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, чтобы сделать это с меньшими накладными расходами. См. отличный пост в блоге от Арнольда Дэниелса по этому вопросу, но обратите внимание, что его пример предназначен для загрузки. Чтобы встроить изображения, извлеките заголовок Content-Disposition (третий вызов header ()).

Надеюсь, это поможет - больше после того, как моя мигрень прояснится.

10 голосов
/ 29 декабря 2009

В примере переписывания Дэна Удей есть две опечатки (и я не могу это прокомментировать), это должно быть:

RewriteCond %{REQUEST_URI} ^/images/cached/
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
RewriteRule (.*) /images/generate.php?$1 [L]

Привет.

5 голосов
/ 26 сентября 2008

Стоит добавить, что ваш код не генерирует «несанкционированные» размеры этих изображений.

Таким образом, следующий URL создаст 200x200 версию изображения 1234, если он еще не существует. Я бы весьма предложил вам убедиться, что запрошенный URL-адрес содержит поддерживаемые вами размеры изображений.

/images/get/200x200/1234.jpg

Злоумышленник может начать запрашивать случайные URL-адреса, всегда изменяя высоту и ширину изображения. Это может привести к серьезным проблемам с вашим сервером, поскольку он будет находиться там, по сути, под атакой, создавая образы размеров, которые вы не поддерживаете.

/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
    }
?>
1 голос
/ 02 марта 2009

Кажется, отличный пост, но моя проблема все еще остается нерешенной. У меня нет доступа к htaccess у моего хост-провайдера, так что нет никаких проблем с настройкой Apache. Есть ли способ установить заголовок cace-control для изображений?

0 голосов
/ 04 апреля 2010

Вместо того, чтобы хранить адрес файла в БД, я предпочитаю добавлять случайное число к имени файла всякий раз, когда пользователь входит в систему. Примерно так для пользователя 1234: image / picture_1234.png? Rnd = 6534122341

Если пользователь отправляет новое изображение во время сеанса, я просто обновляю случайное число.

GUID решает проблему с кэшем на 100%. Однако это как бы усложняет отслеживание файлов изображений. При использовании этого метода есть вероятность, что пользователь снова увидит ту же картинку при следующем входе в систему. Однако вероятность того, что вы сгенерируете случайное число из миллиарда чисел, невелика.

0 голосов
/ 05 марта 2009

Мне удалось сделать это, просто используя заголовок redirect в 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");
}
0 голосов
/ 26 сентября 2008

phpThumb - это фреймворк, который генерирует уменьшенные изображения / эскизы на лету Он также реализует кэширование, и его очень легко реализовать.

Код для изменения размера изображения:

<img src="/phpThumb.php?src=/path/to/image.jpg&w=200&amp;h=200" alt="thumbnail"/>

даст вам миниатюру 200 х 200;

Он также поддерживает водяные знаки.

Проверьте это по адресу: http://phpthumb.sourceforge.net/

0 голосов
/ 26 сентября 2008

Звучит как надежный способ сделать это. Следующим шагом может быть выход за рамки PHP / MySQL.

Возможно, настроить ваши заголовки :

Если вы используете PHP для отправки типов MIME, вы также можете использовать заголовки «Keep-alive» и «Cache-control», чтобы продлить срок службы ваших изображений на сервере и снять часть нагрузки с PHP / MySQL.

Также рассмотрите плагины apache для кэширования. Как mod_expires .

О, еще одна вещь, насколько вы контролируете свой сервер? Должны ли мы ограничить этот разговор просто PHP / MySQL?

0 голосов
/ 26 сентября 2008

Ваш подход кажется вполне разумным - я бы добавил, что необходимо установить какой-то механизм, чтобы проверить, была ли дата, когда была сгенерирована кэшированная версия, после последней измененной временной метки исходного (исходного) файла изображения и, если нет, восстановить кэшированную / измененная версия. Это гарантирует, что если дизайнеры изменят изображение, кэш будет обновлен соответствующим образом.

...