Как я exif данные из JPEG вытаскивал и в БД запихивал (а потом доставал)

от автора

Задача была такова: Есть общая папка с изображениями, к каждому из которых проставлены ключевые слова (определяющие конкретного человека/организацию). Необходимо получать перечень изображений с определенным тегом для последующего вывода галереи на странице этого человека.

Просьба для тех, кто будет читать, особенно с целью критиковать

Основная задумка статьи, помочь новичкам типа меня настроить один из частных случаев взаимодействия exif-php-mysql-php-html.
Я за объективную критику, если у вас есть рациональные предложения по улучшению кода, я с радостью их послушаю и приму к сведению.
Еще раз напомню, что это чуть ли не первое моё написание php-скрипта, где необходимо работать с БД на сервере, поэтому у меня действительно по-тупому всё сделано в одном скрипте.

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

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

Был выработан следующий алгоритм действий:

  1. Получить список файлов (*.jpeg);
  2. Циклом пройти по всем файлам и вытащить exif данные (а именно тег keywords);
  3. Занести имена файлов и ключевые слова в базу данных;
  4. Получение списка файлов по определенному тегу;
  5. Самое приятное — исправление ошибок.

Что происходит дальше пока не сильно важно, потому что основные проблемы родились именно на пункте 4.

Итак, разберемся что у нас происходит на каждом этапе:

1. Получить список файлов (*.jpeg)

Тут особо проблем не возникло, спасибо функции glob();

$filelist = glob("*.jpg"); // получаем список файлов (скрипт лежит в папке с файлами) 

2. Вытащить exif данные (тег keywords)

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

foreach ($filelist as $name) { // Перебираем файлы, имена заносим в переменную $name     $exif = exif_read_data($name, 0, true); // Получаем метаданные из файла     foreach ($exif as $key => $section) { // Циклично получаем все теги метаданных       foreach ($section as $setting => $val) { // Получаем значение тега         if ($key == "IFD0" && $setting == "Keywords") { // Ждем когда доберемся до тега keywords           echo "$key.$name: $val<br />"; // Выводим полученное значение         }       }     } } 

3. Занести полученную информацию в базу данных

Данная статья не про развертывание бд на сервере, поэтому сразу перейдём к делу.

Нам необходимо:

  • Подключиться к БД
     $con = mysqli_connect('<server_name>','<user_login>','<user_pass>', '<database>') or die ("html>script language='JavaScript'>alert('Не удается подключиться к базе данных. Повторите попытку позже.'),history.go(-1)/script>/html>"); // Подключение к БД с выводом ошибки, если подключение не удалось 

  • Создать таблицу (если её нет)
    Я использовал поля Number (порядковый номер/id), filename (имя файла оригинального изображения) и keywords (ключевые слова)
    $sql_ins_first = "CREATE TABLE IF NOT EXISTS test (id int(11) NOT NULL AUTO_INCREMENT, filename varchar(255) CHARACTER SET utf8 NOT NULL, keywords varchar(255) CHARACTER SET utf8 NOT NULL, PRIMARY KEY (id)) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"; // Создание таблицы (если её нет) mysqli_query($con, $sql_ins_first); // Отправить запрос, который описан выше 

  • Добавление записей в таблицу
    $sql_ins_second = "INSERT INTO <table_name> (filename, keywords) VALUES ('$name', '$val')"; // Запрос на добавление записей в таблицу (на каждой итерации цикла) if (mysqli_query($con, $sql_ins_second)) { // Проверка выполнения запроса     echo "$name - New record created successfully<br />"; // Информация успешно занесена в базу } else {     echo "Error: " . $sql_ins_second . "<br>" . $con->error; // Ошибка записи данных в БД } 

4. Получение списка файлов по определенному тегу

Здесь начинается самое интересное, потому что именно отсюда мы плавно втекаем в ошибки, которые устраняются не совсем очевидным образом

Привожу сразу пример моей функции с комментариями

function select_sql($key) // На вход подаём тег, который нам нужен для вывода конкретного набора файлов  {     /* Запрос: Выбрать поле "filename" из таблицы "test",     где символьная строка "keywords" соответствует шаблону '%$key%' */     $sql_select = "SELECT filename FROM test WHERE keywords LIKE '%$key%'";     $result = mysqli_query($con, $sql_select); // Заносим результат в переменную      /* извлечение ассоциативного массива */     while ($row = $result->fetch_assoc()) { // Обрабатываем все полученные записи в результате нашей выборки         echo "<div><p>".$row['filename']."</p><img src=\"".$row['filename']."\" style=\" width: 200px\" /></div>"; // Вывод на страницу имени файла и превью нашего изображения     } } 


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

У меня получилась следующая ситуация.

Полученная строка ключевых слов к изображениям в формате ASCII, подаваемая на вход переменная с нужным тегом также в формате ASCII, но при отправке запроса (не важно, через скрипт или через PhpMyAdmin) на выходе получаем NULL.

Попробовав множество вариантов (среди них обязательно был вариант с использованием utf8_encode(), который, кстати, не работал).

5. Самое приятное — исправление ошибок

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

По факту, мы просто получили ситуацию, когда всё работает (теги записались в БД, запрос собирается правильно, но на выходе мы всё равно имеем пустую выборку).
Логически приходим к выводу, что проблема в кодировке… Но что делать, если стандартная функция utf8_encode() не работает?

Скажу честно, я перепробовал безумно много вариантов, но в итоге у меня родилась идея «с промежуточной кодировкой» — я сначала попробовал перевести из ASCII в другую кодировку, а потом уже в UTF-8, но снова ничего не получалось.
Здесь я почуял неладное, а есть ли какой-то список кодировок и есть ли у меня там что-либо?

Немного погуглив, нашел и попробовал установить в начале скрипта mb_detect_order(«eucjp-win,sjis-win,UTF-8»); и сразу же получил кодировку всех строк в eucjp-win. Осознав, что можно попробовать переставить utf-8 на первое место, у меня всё пошло как по маслу, но было очень важно после считывания метаданных из картинок — экранировать символы.

Представляю вашему вниманию итоговый скрипт:

<?php   mb_detect_order("UTF-8,ascii,eucjp-win,sjis-win"); // Установка списка кодировок    $con = mysqli_connect('<server_name>','<user_login>','<user_pass>', '<database>') or die ("html>script language='JavaScript'>alert('Не удается подключиться к базе данных. Повторите попытку позже.'),history.go(-1)/script>/html>"); // Коннект к БД    $sql_ins_first = "CREATE TABLE IF NOT EXISTS test (id int(11) NOT NULL AUTO_INCREMENT, filename varchar(255) CHARACTER SET utf8 NOT NULL, keywords varchar(255) CHARACTER SET utf8 NOT NULL, PRIMARY KEY (id)) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1";   mysqli_query($con, $sql_ins_first); // Проверка наличия/создание таблицы    $filelist = glob("*.jpg"); // Получение списка файлов в директории с расширением *.jpg    foreach ($filelist as $name) { // Цикл по файлам     $exif = exif_read_data($name, 0, true); // Считывание метаданных     foreach ($exif as $key => $section) { // Цикличный проход по тегам       foreach ($section as $setting => $val) { // Получение значения тега         if ($key == "IFD0" && $setting == "Keywords") { // Проверка соответствия нужного нам тега (keywords)           $val1 = mysqli_real_escape_string($con, $val); // Экранирование символов в полученной строке значений тега Keywords           $val1 = str_ireplace(";", "; ", $val1); // Заменяем ";" на "; " /* При проверке текущего состояния строки после экранирования мы увидим между всеми символами "\0", а это значит, что нам необходимо их удалить, чтобы получить адекватную строку */           $val1 = str_ireplace("\\0", "", $val1); // Заменяем "\0" на "" (пустую строку) и не забываем экранировать слеш            $sql_ins_second = "INSERT INTO test (filename, keywords) VALUES ('$name', '$val1')"; // Запрос на добавление записей в БД  /* Проверка записи данных в БД */           if (mysqli_query($con, $sql_ins_second)) {                echo "$name - New record created successfully<br />";           } else {                echo "Error: " . $sql_ins_second . "<br>" . $con->error;           }         }       }     }   } $con->close(); // Закрыть подключение  /* Объявление функции select_sql() */ function select_sql($key) {   $con = mysqli_connect('<server_name>','<user_login>','<user_pass>', '<database>') or die ("html>script language='JavaScript'>alert('Не удается подключиться к базе данных. Повторите попытку позже.'),history.go(-1)/script>/html>"); // Коннект к БД   $sql_select = "SELECT filename FROM test WHERE keywords LIKE '%$key%'"; // Запрос на выборку данных по определенному тегу   $result = mysqli_query($con, $sql_select); // Заносим результат в переменную    while ($row = $result->fetch_assoc()) { // извлечение ассоциативного массива     echo "<div><p>".$row['filename']."</p><img src=\"".$row['filename']."\" style=\" width: 200px\" /></div>"; // Вывод названий файлов и изображений на страницу   } $con->close(); // Закрыть подключение } $string_key = utf8_encode('test'); // Преобразование переменной в кодировку UTF-8 (на всякий случай) select_sql($string_key); // Вызов функции с нужным тегом ?> 

Скорее всего, второе подключение нам не нужно, потому что по хорошему, его подсасывать с другого php-скрипта, но это в будущем, потому что статья ориентировалась на другой аспект.


И, как говорится:

Спасибо за внимание! Я готов выслушать ваши вопросы!


ссылка на оригинал статьи https://habr.com/ru/articles/592535/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *