Хранение изображений в базе данных MySQL

Для хранения изображений в базе данных MySQL необходимо определить одно из полей таблицы как производное от типа BLOB. Сокращение BLOB означает большой двоичный объект. Тип хранения данных BLOB обладает несколькими вариантами:

  • TINYBLOB - может хранить до 255 байт
  • BLOB - может хранить до 64 килобайт информации
  • MEDIUMBLOB - до 16 мегабайт
  • LONGBLOB - до 4 гигабайт

Соответсвенно, для хранения изображений нам надо создать таблицу images с двумя полями:

  • id - уникальный ID изображения
  • content - поле для хранения изображения

Для сохранения файла изображения в базе данных необходимо прочитать файл в переменную и создать запрос на добавление данных в таблицу. Допустим у нас есть форма для загрузки файла изображения на сервер:

<form enctype="multipart/form-data" method="post" action="putimage.php">
Изображение: <input type="file" name="image" />
<input type="submit" value="Загрузить" />
</form>

Обработчик формы - файл putimage.php:

<?php
// Проверяем пришел ли файл
if( !empty( $_FILES['image']['name'] ) ) {
  // Проверяем, что при загрузке не произошло ошибок
  if ( $_FILES['image']['error'] == 0 ) {
    // Если файл загружен успешно, то проверяем - графический ли он
    if( substr($_FILES['image']['type'], 0, 5)=='image' ) {
      // Читаем содержимое файла
      $image = file_get_contents( $_FILES['image']['tmp_name'] );
      // Экранируем специальные символы в содержимом файла
      $image = mysql_escape_string( $image );
      // Формируем запрос на добавление файла в базу данных
      $query="INSERT INTO `images` VALUES(NULL, '".$image."')";
      // После чего остается только выполнить данный запрос к базе данных
      mysql_query( $query );
    }
  }
}
?>

Извлечь сохраненный файл изображения можно следующим образом (файл image.php):

<?php
if ( isset( $_GET['id'] ) ) {
  // Здесь $id номер изображения
  $id = (int)$_GET['id'];
  if ( $id > 0 ) {
    $query = "SELECT `content` FROM `images` WHERE `id`=".$id;
    // Выполняем запрос и получаем файл
    $res = mysql_query($query);
    if ( mysql_num_rows( $res ) == 1 ) {
      $image = mysql_fetch_array($res);
      // Отсылаем браузеру заголовок, сообщающий о том, что сейчас будет передаваться файл изображения
      header("Content-type: image/*");
      // И  передаем сам файл
      echo $image['content'];
    }
  }
}
?>

Чтобы вывести изображение в HTML-документе, делаем так:

<img src="image.php?id=17" alt="" />

И последнее: графические файлы иногда имеют довольно большой размер, убедитесь, что настройки сервера позволяют работать с таким объемом данных. В файле php.ini это директивы post_max_size - определяет максимальный объем данных передаваемых методом POST, и upload_max_filesize - определяет максимальный размер загружаемого файла. Так же проверьте, позволяют ли настройки MySQL обрабатывать запросы с большим объемом данных (директива max_allowed_packet файла my.ini).

Комментариев: 59

  1. art:

    еще бы примеры, когда хранить картинки в базе лучше чем просто на диске

  2. admin:

    art, хранить графические файлы в базе данных - не самое удачное решение. Ведь это создает дополнительную нагрузку на сервер БД.

    Когда мы сохраняем в базе данных каталог продукции, заказы в Интернет-магазине - то это вполне оправдано. Потому как альтернативный вариант - сохранять всю информацию в файлах. И работать с файлами напрямую. Но базы данных для того и созданы, чтобы избавить нас от этой рутины. Ведь в конечном итоге, все данные все равно сохраняются в файлах, хотя мы и говорим, что “данные хранятся в БД”. БД - это некий уровень абстракции, который здорово облегчает жизнь.

    Почему тогда хранят файлы в базе данных? Дело в том, что на некоторых хостингах на объем дискового пространства есть ограничения, в то время как на размер БД - нет. Подчеркиваю - на некоторых. В этом случае, чтобы не выйти за пределы дисковой квоты прибегают к такой хитрости - хранят файлы в БД. В противном случае смысла в этом нет.

  3. Владимир Лапшин:

    “SELECT *” в реально работающем приложении не самая хорошая идея. :) Лучше все-таки перечислять нужные поля. Точно помогает, когда в таблицу приходится добавить еще какое-то поле. :)

  4. admin:

    “SELECT *” в реально работающем приложении не самая хорошая идея.

    Согласен, сам читал об этом, но частенько забываю. Здесь это тем более актуально - нам нужно выбрать всего одно поле. Исправил.

  5. Анатолий:

    Здравствуйте. Помогите начинающему. Загрузка картинки в базу идет нормально (вроде бы), т.е. объем загруженного файла (8кб) соответствует объему картинки, но плучить изображение обратно я не могу. В приведенном вами файле image.php - $_GET - не существует. Я малость его изменил - убрал лишние пока проверки - в результате, файл выводится, но не в виде картинки, а в виде текста-абракадабры.
    Привожу свой вар. подскажите где ошибка, если знаете.

    <?php
    // Отсылаем браузеру заголовок, сообщающий о том,
    //что сейчас будет передаваться файл изображения
    header("Content-type: image/*");
    include_once 'conf4b.php';//подключаем базу

    //Вывод изображения из БАЗЫ, загруженного из формы
    $query = "SELECT content FROM image ";
    // Выполняем запрос и получаем файл
    $res = mysql_query($query);
    $image = mysql_fetch_array($res);
    echo $image['content'];
    //выводится пустой квадратик
     
    mysql_close($connect)or die("not razriva");
    print "соединение разорвано";
    ?>
  6. admin:

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

    print "соединение разорвано";

    уже является ошибкой. Браузер считает строку “соединение разорвано” частью изображения.

  7. Анатолий:

    Я полагал, что перед header нельзя делать какой-либо вывод.
    Впрочем, я убрал принт, оставил только одно echo - результат тот же.

  8. admin:

    Перед заголовками, разумеется, не должно быть вывода. Сервер и браузер общаются между собой при помощи заголовков. В данном случае ты отправляешь браузеру заголовок “Content-type: image/*”, сообщающий браузеру, что ВСЕ ДАЛЕЕ будет картинка. Но вместе с картинкой отправляешь еще и текст. По поводу того, почему не работает - причин может быть воз и маленькая тележка:
    1. скрипт для коннекта к БД содержит после закрывающего тега ?> какой-нибудь вывод (например, пробел или перевод строки)
    2. пробел или пустая строка перед открывающим тегом <?php в файле image.php - опять вывод в браузер информации
    3. блокнот Windows при использовании кодировки Unicode добавляет в начало файла служебный символ Byte Order Mark - а это тоже вывод в браузер
    4. в базу данных был записан поврежденный файл изображения

    P.S. Такие вопросы лучше обсуждать на форуме.

  9. Анатолий CMX:

    Спасибо! То что нужно! Все работает без правок и добавлений. Как семпл очень полезно и информативно!

    От себя могу добавить, что хранение изображений в базе данных актуально например когда:
    1) используются каталоги изображений для шаблонов (например наборы иконок)
    2) когда есть много мелких изображений, файлы которых “весят” очень мало, но самих файлов очень много. В базе будет занято ровно столько места сколько требуется, а не сектора диска под нефактический объем файла.

    НО:
    Если вы собрались делать фото галерею или каталог “крупных” изображений, то целесообразнее организовать каталог в виде папок и подпапок на диске, а уже ссылки на файлы можно хранить в базе! Т.к. при достаточно большом объеме файлов и их количестве, если хранить данные в базе, возрастет нагрузка на ресурсы сервера при обращении и получениии данных из базы.

    :) не знаю толково или нет, но думаю что прокомментировал в тему!

  10. Влад:

    Мне понравилось про () , люблю изощренные решения. но всё таки мне не понятно почему тот же пхп не сделал стандартную функцию для чтения бинарных изображений из БД. явно напрашивается такая штука.

  11. victor:

    Хранить изображения в базе очень удобно. Насчёт загрузки сервера - это миф. Использую MSSQL 2005 и RAID 5, база около 20Г.

  12. admin:

    victor, а в чем удобство-то? Какое реальное преимущество мы получаем, если изображение у нас в таблице БД, а не просто в директории?

  13. victor:

    В администрировании, бекап, безопасность, механизм построения галереи простой. То есть я имею введу необходимые действия удаления, изменение, добавления галерей и фото. Всё это элементарно организуется посредствам самой ДБ (cascade) и требует минимального кода. В случае хранения фото “на диске” алгоритм сильно усложняется, нужно выполнять множество проверок при выполнении операций с диском… MSSQL сильно отличается от MySQL , а именно структура хранения данных, не рискнул бы хранить в ней большое количество фото.

  14. Дмитрий:

    Убогое какое-то описание, а где в скрипте соединение с базой данных, выбор таблицы. Уж если описываете, так сделайте чтобы даже военным было понятно :)

  15. Сергей:

    при переходе с MSSQL на MySQL столкнулся с проблемой, select count(*) from image; (посчитать количество картинок в базе) на MySQL выполняется 25 сек, на MSSQL MySQL меньше секунды. в таблице 50000 картинок, 300MB.
    это нормально для mysql? или я что-то не так делаю?

  16. admin:

    Сергей, здесь явно что-то не так. Не может быть, чтобы разница во времени выполнения была такой большой. Попробуйте задать свой вопрос на форумах
    SQL.RU
    SQLINFO.RU

  17. Mujeek:

    Преимущества хранения изображений в базе - на лицо… когда есть необходимость каскадного удаления данных, а изображения находится, например, на 3 уровне подчинённости удаляемой записи..

  18. Mujeek:

    вдобавок не нужны проверки существования в каталоге одноименного файла…

  19. Mujeek:

    Будут ли кэшироваться на клиенте изображения, полученные из базы данных,или при каждом открытии страницы все изображения закачиваются заново?

  20. admin:

    Mujeek, вообще, дело обстоит так
    * Страницы передаваемые по POST никогда не сохраняются в кэш.
    * Страницы запрашиваемые по GET и содержащие параметры (в URL присутствует ‘?’) не сохраняются в кэш, если не указано обратное.
    Наш вызов скрипта картинки выглядит так
    http://server.com/image.php?id=17
    а значит по правилам изображение не будет сохраняться в кэш (присутствуют параметры), но через заголовок можно управлять этим. Подробности здесь:
    PHP и Web. Кэширование

  21. Ренат:

    Здравствуйте, хочу спросить, а средствами PHP можно вывести на экран вместе с картинкой ещё, к примеру, оформление сайта? Ну не только картинку на экран? Или это уже к JavaScript’y?

  22. Mujeek:

    И как получить увеличенную картинку в отдельном окне при клике на изображении?

  23. hannibal:

    Очень нужно!!!! Подскажите как вывести несколько изображений из базы???

  24. Максим:

    Сделал, подобие примера. Точнее повторил все точно точно также но вот вывода картинки я не вижу.
    Загружаеться но вывода нет.
    В чем может быть проблема? картика 64кб, поле(блоб).

    qip 439_810-757 Мучаюсь 2 день, прошу помощи.

  25. admin:

    И как получить увеличенную картинку в отдельном окне при клике на изображении?
    <a href=”/images/big.jpg” target=”_blank”
    onClick=”popupWin = window.open(this.href, ”, ‘location,width=500,height=200,left=200,top=200′); popupWin.focus(); return false;”>
    <img src=”/images/small.jpg” alt=”" />
    </a>
    А если красиво - использовать готовое решение:
    Плагин FancyBox для библиотеки jQuery

  26. admin:

    Максим, давайте обсудим это на форуме. Выкладывайте свой скрипт, посмотрим, в чем там проблема.

  27. Евгений:

    > при переходе с MSSQL на MySQL столкнулся с проблемой,
    > select count(*) from image; (посчитать количество
    > картинок в базе) на MySQL выполняется 25 сек, на
    > MSSQL MySQL меньше секунды. в таблице 50000 картинок, 300MB.
    > это нормально для mysql? или я что-то не так делаю?

    Не так делаете. Зачем запрашивать все поля *?
    Проще и быстрее будет SELECT COUNT(`id`) FROM `image`;

  28. Sancho:

    do {
      printf ("Ф.И.О. - %s %s Дата визита: %s",
      $myrow['id'],$myrow['username'], $myrow['username'],$myrow['namelastname'],$myrow['lastvisit']);
    } while ($myrow = mysql_fetch_array($result,$db));
  29. Sancho:

    не пойму почему не передает полный текст

  30. admin:

    Sancho, а Вы о чем вообще говорите?

  31. Ольга:

    Скажите, что делать с хедером, если изображение мне нужно вывести в середине страницы и, естественно, заголовок уже выведен.
    При инклюде image.php, вылезает ошибка - Warning: Cannot modify header information - headers already sent by (output started at Z:\home\localhost\www\xyxa\index.php:17) in Z:\home\localhost\www\xyxa\image.php on line 13
    Как мне всё же вывести изображение из БД в index.php?
    (И, мало ли у кого есть готовый пример для ресайза изображения ДО добавления его в БД - буду очень признательна. Хотелось бы из одной загружаемой человеком фотографии сделать превью и фото, к примеру 600х400 и поместить их в БД, причем учитывая, что просто уменьшение может оказаться не пропорциональным, если, к примеру, фото было 1200х1000)Спасибо.

  32. admin:

    если изображение мне нужно вывести в середине страницы и, естественно, заголовок уже выведен. При инклюде image.php, вылезает ошибка - Warning: Cannot modify header information
    По-моему, в заметке четко написано: чтобы вывести изображение в HTML-документе, делаем так

    <img src="image.php?id=17" alt="" />

    Включать изображение на страницу с помощью include?! Даже не комментирую…

    у кого есть готовый пример для ресайза изображения ДО добавления его в БД
    Масштабирование изображений

  33. Ольга:

    Включать изображение на страницу с помощью include?! Даже не комментирую…
    Спасибо за быстрый ответ, но Вы не поняли, я не изображение же вставляю include, а файл image.php, а в самом html коде, конечно же.

    Мне не понятна вот эта часть Вашего кода:
    // Отсылаем браузеру заголовок, сообщающий о том, что сейчас будет передаваться файл изображения
    header(”Content-type: image/*”);

    Это же получается второй header и выдается ошибка ( Если не сложно, пожалуйста, объясните? Я все сделала так, как Вы указали в коде, затем весь пхп в отдельном файле image.php я добавила в индексную страницу в то место, где планируется вывод картинки. (Пробовала и не отдельным файлом image.php делать, а втавлять код сразу в страницу, где оно выводится), но результат один и тот же:
    Cannot modify header information - headers already sent by (output started at …
    Заранее спасибо.

  34. admin:

    но Вы не поняли, я не изображение же вставляю include, а файл image.php, а в самом html коде, конечно же
    Я прекрасно понял, что с помощью include вставляется файл image.php. И именно поэтому сказал “Даже не комментирую…”. Еще раз для особо сообразительных - файл index.html (или index.php), где надо вывести изображение:

    <?php
    // здесь какой-то код
    ?>
    <html>
    <head>
    .....
    </head>
    <body>
    <?php
    // здесь еще какой-то код
    ?>
    Изображение:<br/>
    <img src="image.php?id=17" alt="" />
    <?php
    // и еще немного
    ?>
    </body>
    </html>
  35. Ольга:

    Не злитесь, пожалуйста, я только учусь (( Я поняла свою ошибку, спасибо. Правда изображение все равно не выходит, но уже нет того, что было )) Буду разбираться дальше. СПАСИБО!!!

  36. admin:

    Правда изображение все равно не выходит
    Что, Данила-мастер, не выходит каменный цветок?
    Вывод картинок из БД - там есть рабочий пример.

  37. Ольга:

    ))) С таким огромным трудом!!!
    Но, благодаря Вам - легче! ))

  38. Алексей:

    А у меня такой вопрос - при выводе изобажения указываю хедеры, но вместо отображения изображения, происходит вывод диалога на сохранение или открытие файла, что с этим делать?

  39. admin:

    Алексей, чтобы появился диалог на сохранение или открытие файла, надо в скрипте отправить заголовок

    header('Content-disposition: attachment; filename="image.jpg"');

    Если Вы не отправляли такой заголовок - для меня полная загадка, откуда взялся диалог.

  40. Sancho:

    Подскажите пожалуйста - в форму вставки изображения можно добавить текстовые поля, чтобы их передать одновременно в фаил обработчик?

  41. Sancho:

    Да с изображением все работает отлично огромное спасибо!!!!

  42. admin:

    в форму вставки можно добавить текстовые поля
    Почему нет? Вот здесь можно посмотреть рабочий пример: Вывод картинок из БД

  43. Sancho:

    Тогда прости за наглость есче вопрос как ограничить на размер загружаемой картинки

  44. admin:

    как ограничить на размер загружаемой картинки
    На стороне клиента - никак. А на сервере, после того, как изображение будет загружено во временную директорию, мы можем узнать его размер из переменной $_FILES['uploadfile']['size'].
    Загрузка файлов на сервер
    Если этот размер больше допустимого, мы просто выдаем сообщение пользователю “Ошибка: mах размер файла - …”, а файл во временной директории будет удален автоматически после завершения работы обработчика формы. Кроме того, есть ограничения на max размер POST-данных и размер загружаемого файла:
    Загрузка больших файлов на сайт

  45. Sancho:

    Огромное человеческое СПАСИБО!!!

  46. Sancho:

    Можно еще один вопрос. Можно ли дать пользователю, просмотреть в браузере, загружаемое изображение до отправки на сервер! Чтобы он увидел сразу как оно будет выглядеть

  47. admin:

    Можно ли дать пользователю, просмотреть в браузере, загружаемое изображение до отправки на сервер
    Нет.

  48. Sancho:

    Ответ исчерпываюше но и нато большое спасибо не будем питатся и тратит время отсутсвие результата ето тоже результат!

  49. lake:

    А что надо поменять, если нужно загрузить в БД не картинку, а файл xls?

  50. admin:

    А что надо поменять, если нужно загрузить в БД не картинку, а файл xls
    Поле BLOB предназначено для хранения бинарных данных; не имеет значения, что это будет - изображение, PDF-документ или таблица Excel.

  51. Андрей:

    здравствуйте!
    Подскажите пожалуйста, почему то фото не добавляется в БД. Вручную вставил фото в БД 250 кб, при этом БД жутко тормозит (страница грузилась с минуту, хотя выполнение запроса пишет 0.3 сек), и фото не выводит. Код image.php в точности как у Вас, а в файле индекс, где должно вывестись фото одна строчка

  52. Андрей:

    прошу прощения. Одна проблемма снялсь, фото вывелось. Может, подождать чуток надо было). А вот закачки в БД так и не происходит. Нажимаю “загрузить”, начинается загрузка, и ничего не пишет. Ни ошибок, ни что загружено. Так и должно быть?
    И подскажите пжл, какие значения полей в табл. имагес должны быть?

  53. admin:

    Андрей, рабочий пример можно взять здесь: Вывод картинок из БД. Если что-то не работает, давайте обсудим на форуме.

  54. Света:

    Здравствуйте. Подскажите пожалуйста,
    как после запроса
    $query = “INSERT INTO `images` VALUES(NULL, ‘”.$image.”‘)”;
    определить автоматически присвоенный ID?

    Хочу вывести изображение добавленное только что пользователем вместе с другими (текстовыми) полями, т.е пользователь должен увидеть загруженные им данные включая изображение (только его данные и изображение)

    Короче вместо id=17 хочу использовать переменную $id

  55. admin:

    как определить автоматически присвоенный ID?
    Функция mysql_insert_id().

  56. Александр:

    а как узнать ширину и высоту загруженной картинки в базе?

  57. slogic:

    Пример записи в базу не очень удачный. Картинка, не смотря на то, что исходно находится на диске, грузится полностью в память, и лишь потом в базу. Это не проблема, когда кратинка равна 250КБ, однако она может быть и 4МБ и больше. Тогда вы столкнетесь с нехваткой памяти на дешевых хостингах. Поэтому неплохо было бы дополнить пример загрузкой бинарных данных прямо с диска. Возможно такое на MySQL? :)

  58. Ольга:

    Здравствуйте! Я новичок, и что-то не совсем поняла как мне создать таблицу в базе, напишите пример пожалуйста!

    Вот так или возможно я ошибаюсь:

    CREATE TABLE `ad_e_mail` (
    `id` int(5) NOT NULL auto_increment,
    `content` varchar(255) NOT NULL default ”,
    PRIMARY KEY (`id`)
    ) TYPE=MyISAM ;

  59. Антон:

    1. Это непроизводительно - дополнительная нагрузка на БД.
    2. Картинки отдаются клиенту как файлы - пусть хранятся как файлы на диске. Это намного быстрее, чем вынимать картинки из базы.
    3. Кэширование.
    4. Хочется попробовать - попробуйте, но вот серьезные вещи так не делают подробнее тут http://digger3d.com/webmaster/mysql-php-drupal-shell-tips/replace-image-in-mysql-database.html

Оставьте свой отзыв