Отдача изображения с подменой URL

Интересует такой вопрос: (пример на сайте imgur.com)
К примеру есть изображение:
http://imgur.com/3vwoV

Адрес к картинке:
http://i.imgur.com/3vwoV.jpg

А картинка лежит на сервере по такому пути:
site.ru/images/2011/09/3vwoV.jpg

Собственно, вопрос:
Как правильно отдавать картинку ?

Как сделал я:
В папке /images/ создал файл .htaccess с таким содержанием:
/** * GeSHi (C) 2004 - 2007 Nigel McNie, 2007 - 2008 Benny Baumann * (http://qbnz.com/highlighter/ and http://geshi.org/) */ .csharp.geshi_code {font-family:monospace;} .csharp.geshi_code .imp {font-weight: bold; color: red;} .csharp.geshi_code .kw1 {color: #0600FF;} .csharp.geshi_code .kw2 {color: #FF8000; font-weight: bold;} .csharp.geshi_code .kw3 {color: #008000;} .csharp.geshi_code .kw4 {color: #FF0000;} .csharp.geshi_code .kw5 {color: #000000;} .csharp.geshi_code .co1 {color: #008080; font-style: italic;} .csharp.geshi_code .co2 {color: #008080;} .csharp.geshi_code .co3 {color: #008080;} .csharp.geshi_code .coMULTI {color: #008080; font-style: italic;} .csharp.geshi_code .es0 {color: #008080; font-weight: bold;} .csharp.geshi_code .es_h {color: #008080; font-weight: bold;} .csharp.geshi_code .br0 {color: #000000;} .csharp.geshi_code .sy0 {color: #008000;} .csharp.geshi_code .st0 {color: #666666;} .csharp.geshi_code .st_h {color: #666666;} .csharp.geshi_code .nu0 {color: #FF0000;} .csharp.geshi_code .me1 {color: #0000FF;} .csharp.geshi_code .me2 {color: #0000FF;} .csharp.geshi_code span.xtra { display:block; }

RewriteRule ^([a-zA-z0-9]{6}).jpg$ show.image.php?&key=$1 [L]
 


show.image.php
Тут скрипт делает запрос в базу, получает верный путь и отдает картинку через GD (а значит, каждый раз катинка будет генерироваться, подгружаться в память и т.д).

Как можно сделать лучше, не нагружая озу ?

1 ответов


Отдачу картинки естественно лучше сделать без запросов к базе и PHP. Это нагружает сервер.

Но когда нужно сделать запрос к базе и какие-то изменения в картинке есть два способа, как это сделать лучше.
Первый более правильный:
Перед Apache поставить Nginx (вообще он всегда должен стоять перед апачем) и указать, что запросы по определённому url должны складываться в кеш Nginx на несколько часов.

Второй - без правки конфигов веб-сервера:
Пользователь делает запрос к картинке. Если картинка не найдена, то запрос переадресуется к PHP (такое легко делается на mod_rewrite), PHP делает нужные манипуляции с картинкой и создаёт её по нужному адресу. При следующем запросе картинка будет отдаваться напрямую. Но тут большая проблема с тем, что могут сто раз одновременно запросить картинку, когда её нет и она в сто потоков будет пересоздаваться.

И главное помнить, что если нехороший пользователь подставит какие-то символы (например, после вопросильного знака ?example=primer), то картинка всёравно должна браться из кеша.

И ещё. Много встречал сайтов, где при отдаче картинке в url передаётся нужный размер, например example.org/images/100x100/image.png - тут главное не дать пользователю подменять размер. У Вас должен быть список разрешённых рамеров, а то так Вам сервер повесят.


После генерации картинки через GD - сохранить результат в файл и при повторном обращении отдавать уже готовую картинку без ГД. Другое дело в том, что в любом случае, если картинка будет отдаваться через ПХП, то она не будет считаться статикой (а значит на ее отдачу нельзя будет настроить тот же nginx) и будет жраться больше ресурсов. Вообще, такое есть смысл делать только если нужно на 100% запретить доступ к картине (то есть отдельным юзерам разрешаем, а всем остальным запрещаем, в том числе и по прямой ссылке). Но такое не делают даже всякие вконтакте, гугл+ и т.д. То есть ссылку по прямой кратинке всегда можно показать кому угодно.

По поводу GD.
Я так понимаю ты GD используешь чисто чтоб открыть картинку и отобразить ее на экран или все же ресайзишь как-то? Если не ресайзишь или будешь кешироват ресайзенную картинку то лучше заюзать какой-то такой скрипт:


<?
$fullPath = $_SERVER["DOCUMENT_ROOT"]."/test/test.jpg";

if ($fd = fopen ($fullPath, "r")) {
    $fsize = filesize($fullPath);
    header("Content-type:  image/jpg"); // не забываем тут контент тайп менять, если картинка не JPG
    header("Content-length: $fsize");
    header("Cache-control: private");
    while(!feof($fd)) {
        $buffer = fread($fd, 2048);
        echo $buffer;
    }
}
fclose ($fd);
exit;
?>
 

Как правильно отдавать картинку ? - без запросов к бд.
Т.е. у вас задача изначально подразумевает неудачную архитектуру.

Храните файлы по другому :)