Архив за Сентябрь 2008

Хранение изображений в базе данных 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).

Проверка правильности заполнения формы

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

JavaScript, представленный немного ниже, позволяет выполнять автоматическую проверку на стороне клиента. Чтобы воспользоваться этим кодом, достаточно просто подключить его к HTML-странице, определить CSS-стили для выделения полей, содержащих некорректную информацию, и добавить дополнительные атрибуты к элементам формы. Чтобы сделать поле обязательным к заполнению, достаточно просто добавить к нему атрибут required. Чтобы выполнить проверку правильности с помощью регулярного выражения, следует добавить атрибут pattern и присвоить ему текст регулярного выражения.

<script src="validate.js"></script> <!-- Подключить модуль проверки -->
<style type="text/css">
/*
Validate.js требует, чтобы были определены стили класса "invalid"
для отображения полей с некорректными данными, давая тем самым
пользователю отличать их визуально.
Для полей с корректными данными можно также определить необязательные стили.
*/
input.invalid { background: #faa } /* Красноватый фон для полей с ошибками */
input.valid { background: #afa } /* Зеленовытый фон для полей, заполненных правильно */
</style>
<!--
Теперь, чтобы включить проверку полей формы, нужно просто
установить атрибут required или pattern.
-->

<form>
<!-- Это поле должно быть заполнено -->
Имя: <input type="text" name="name" required /><br/>
<!--
\s* - означает необязательный пробел
\w+ - один или более алфавитно-цифровых символов
-->

Электронная почта: <input type="text" name="email" pattern="^\s*\w+@\w+\.\w+\s*" /><br/>
<!-- \d{6} означает, что должно быть введено ровно шесть цифр -->
Почтовый индекс: <input type="text" name="zip" pattern="^\s*\d{6}\s*" /><br/>
<!-- Следующее поле не проверяется -->
Непроверяемое поле: <input type="text" /><br/>
<input type="submit" value="Отправить" />
</form>

Обратите внимание: при подключении файла validate.js к HTML-файлу в глобальное пространство имен не добавляется ни одного имени, кроме того, скрипт автоматически регистрирует обработчик события onload, который выполняет обход всех форм документа, отыскивает поля с атрибутами required и pattern и в случае необходимости добавляет обработчики событий onchange и onsubmit. Эти обработчики устанавливают значение свойства className каждого элемента формы, который подвергается проверке, в значение “invalid” или “valid”, поэтому необходимо предусмотреть определение хотя бы “неправильного” (invalid) CSS-класса, чтобы обеспечить визуальное отличие полей с корректными и некорректными данными.

/**
validate.js: ненавязчивая проверка HTML-форм.

После загрузки документа данный скрипт сканирует документ в поисках
HTML-форм и текстовых полей в формах. Если обнаруживаются элементы
с атрибутами "required" или "pattern", к ним добавляются соответствующие
обработчики событий, выполняющие проверку данных формы.

Если атрибут формы имеет атрибут "pattern", значение этого атрибута
используется регулярное JavaScript-выражение, а элементу назначается
обработчик события onchange, который проверяет ввод пользователя с помощью
этого шаблона. Если данные не соответствуют шаблону, цвет фона элемента
ввода изменяется, чтобы привлечь внимание пользователя.

По умолчанию текстовое поле должно содержать некоторую подстроку, которая
соответствует шаблону. Если требуется указать более строгое соответствие,
используйте якорные элементы ^ и $ в начале и конце шаблона.

Элемент формы с атрибутом "required" должен содержать какое-либо значение.
Если быть более точным, атрибут "reqiured" является краткой формой атрибута
pattern="\S". То есть этот атрибут требует, чтобы поле содержало хотя бы
один символ, отличный от пробела.

Если элемент формы прошел проверку, в атрибут "class" этого элемента
записывается значение "valid". В противном случае - значение "invalid".
Для коррекной работы validate.js необходимо вместе с ним использовать
таблицу CSS-стилей, где определяются стили для "неправильного" класса.
Например

<!-- Для привлечения внимания окрасить фон элементов формы,
     содержащих ошибки, в оранжевый цвет -->
<style type="text/css">input.invalid {background: #fa0; }</style>

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

Этот код не может использоваться для проверки форм или полей, в которых
вы определили собственный обработчик событий onchange или onsubmit,
а также полей, для которых вы определили свое значение атрибута class.

Весь программный код размещается внутри анонимной функции
и не определяет ни одного имени в глобальном пространстве имен.
**/


(function() { // Все, что требуется, выполняется внутри анонимной функции
    // По окончании загрузки документа вызвать функцию init()
    if (window.addEventListener)
        window.addEventListener("load", init, false);
    else if (window.attachEvent)
        window.attachEvent("onload", init);

    // Устанавливает обработчики событий для форм и элементов форм,
    // где это необходимо
    function init() {
        // Цикл по всем формам в документе
        for(var i = 0; i < document.forms.length; i++) {
            var f = document.forms[i]// текущая форма

            // Предположить, что форма не требует проверки
            var needsValidation = false;

            // Цикл по всем элементам текущей формы
            for(j = 0; j < f.elements.length; j++) {
                var e = f.elements[j]// текущий элемент

                // Интерес представляют только поля <input type="text">
                if (e.type != "text") continue;

                // Проверить, имеются ли атрибуты, требующие проверки
                var pattern = e.getAttribute("pattern");
                // Можно было бы использовать e.hasAttribute()
                // но MS IE не поддерживает его
                var required = e.getAttribute("required") != null;

                // Атрибут required - это лишь краткая форма записи
                // атрибута pattern
                if (required && !pattern) {
                    pattern = "\\S";
                    e.setAttribute("pattern", pattern);
                }

                // Если элемент требует проверки,
                if (pattern) {
                    // проверять при каждом изменении содержимого элемента
                    e.onchange = validateOnChange;
                    // Запомнить, чтобы потом добавить обработчик onsubmit
                    needsValidation = true;
                }
            }

            // Если хотя бы один элемент формы требует проверки,
            // то необходимо установить обработчик события onsubmit формы
            if (needsValidation) f.onsubmit = validateOnSubmit;
        }
    }
         
    // Эта функция - обработчик события onchange для текстового поля, которое
    // требует проверки. Не забывайте, что в функции init() мы преобразовали
    // атрибут required в pattern
    function validateOnChange() {
        var textfield = this;                            // текстовое поле
        var pattern = textfield.getAttribute("pattern"); // шаблон
        var value = this.value;                          // данные, введенные пользователем

        // Если значение не соответствует шаблону, установить
        // значение атрибута сдфыы равным "invalid"
        if (value.search(pattern) == -1) textfield.className = "invalid";
        else textfield.className = "valid";
    }

    // Эта функция - обработчик события onsubmit для любой формы,
    // требующей проверки
    function validateOnSubmit() {
        // Перед отправкой формы выполнить проверку всех полей в форме
        // и установить их свойства className в соответствующее значение.
        // Если хотя бы одно из этих полей содержит ошибку, вывести диалоговое
        // окно и заблокировать отправку данных формы.
        var invalid = false// Предполагаем, что все правильно
        // Цикл по всем элементам формы
        for(var i = 0; i < this.elements.length; i++) {
            var e = this.elements[i];
            // Если элемент - это текстовое поле, для которого установлен
            // наш обработчик события onchange
            if (e.type == "text" && e.onchange == validateOnChange) {
                e.onchange(); // Вызвать обработчик для повторной проверки
                // Если проверка не пройдена - значит вся форма не прошла проверку
                if (e.className == "invalid") invalid = true;
            }
        }

        // Если форма не прошла проверку, вывести диалоговое окно
        // и заблокировать отправку формы
        if (invalid) {
            alert("Форма заполнена не полностью или были введены некорректные данные.\n" +
                  "Пожалуйста, проверьте правильность выделенных полей и повторите попытку.");
            return false;
        }
    }
})();

Скелет в шкафу, или сайт за 200 долларов

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

Зачем мы их делаем? Сначала — потому что хочется хоть что-то добавить в список работ из 2-х пунктов, почувствовать себя «настоящим вебмастером». Потом — потому, что портфолио уже вроде есть, а имени нет, и денег нет, и ты пишешь код случайному заказчику потому что у него сегодня «настроение сделать сайт» своему ларьку. Позже — потому что друг попросил помочь знакомому — «он хороший парень, но денег нет совсем, он конечно немного заплатит…». Или просто кончились деньги, а тут звонит по объявлению очередной районный графоман с мыслью увековечить свой талант к пятнице, и непременно на желтом фоне.

Больше половины таких творений умирает сама — они никому не были нужны в момент создания, они превращаются в обузу владельцам уже через пару недель, заказчик и не знал толком — на кой ему оно надо, просто несолидно сейчас без сайта, это он по телевизору слышал. Как новая, недорогая и бестолковая игрушка — поигрался, выкинул, теперь можно цедить сквозь губу — «делали мол и мне сайт, толку никакого, лучше бы в кабак сходил».

И сайт висит на оплаченном на год вперед хостинге, с пустыми разделами, и 3 абзаца «Lorem ipsum» в разделе «О компании», и 4 счетчика, которые заказчик тщательно выбирал — лучшие из лучших, не каждому рейтингу можно доверить нас считать, это же лицо страшно сказать — нашей корпорации!

Меньшая часть имеет гораздо более тяжелую судьбу, как у жабы-бумеранга — брошенная, она через некоторое время возвращается и начинает душить.

«Хороший парень», ТЗ которому ты сам писал по его невнятному описанию в телефонной трубке, начинает звонить и жаловаться, что «этот модуль я представлял несколько по другому, и кажется мы договаривались что еще будет каталог, да и я вот еще подумал, ты же быстро сделаешь, да, что неплохо бы в нем…» — ну как отказать такому милашке, он же профессионально хороший парень? А сайт и правда получился хороший, только он уже не 200 стоит, и не тысячу, и бросать жалко.

Дядя из ларька приезжает на своей бэхе и долго и нудно крутит пальцы, не в состоянии объяснить даже самому себе — почему он за две сотни хочет требовать от тебя 400% роста продаж, но мечты, мечты — ведь так хотелось, и потом, я же заплатил, где посетители?

И знакомый знакомых, заслуженный учитель, которому ты нарисовал хомяка бесплатно, и 3-й год дорисовываешь то одно, то другое — он негодует и недоумевает — оказывается ты еще весной обещал перевести на английский его уникальную авторскую методику и его должны были пригласить в Оксфорд читать лекции, «это возмутительно, молодой человек, я всякое видал, но…». А ты давно уже не молодой, но он привык что вокруг все студенты и все должны ему, а он — никому не должен, и вообще это хамство, не выполнять своих обещаний!

И проходит день, наступает вечер, ты понимаешь что 12 часов уже за компом, глаза красные, ты ничего не сделал из того, что планировал, зато внес 5 правок, дописал 2 модуля, восстановил кому-то базу, еще где-то завел дополнительно 2 почтовых адреса, полчаса консультировал знакомую друзей знакомого, не заработал ни копейки, и завтра оказывается ты будешь работать еще, потому что ты чувствуешь себя перед кем-то виноватым, и кому-то ты должен, ты же обещал?

Жаба-бумеранг чувствует себя превосходно, а к тебе во сне приходят невинные души сайтов, умерших во младенчестве, и умоляют проверить — как изменился их ТИЦ и PR после апдейта.

Источник: http://www.legco.net/entry-89.php