Элегантное чтение больших XML файлов




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2024-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

19 Comments

  1. Yashazz

    Может, я торможу, но я правильно понял, что это просто насадка на то же самое последовательное чтение?

    Reply
  2. yku

    А что с процедурой «Прочитать»? Я как понимаю, там и циклы и условия?

    Reply
  3. kapustinag

    (0) Мне нравится. Да, это насадка, но красивая.

    Reply
  4. Evil Beaver

    Поздравляю, вы изобрели XDTO.

    Reply
  5. rtnm

    (4) Evil Beaver, А минус то за что? Расскажешь?

    Reply
  6. a1ex4ndr

    А на каком размере файла тестировалось? А то в тесте файл из трех строчек…..

    Reply
  7. rtnm

    (6) a1ex4ndr, Дело не в размере файла, а в подходе — последовательное чтение. Никаких чудес не будет, все сопоставимо с ЧтениеXML.

    Reply
  8. DJDUH

    Интересно, а как же он элегантно читает телефоны?

    ,,,,,,,,,,,,,,,,,,,,,,,,,,,
    <Телефон>111-11-11</Телефон>
    <Телефон>222-22-22</Телефон>
    ,,,,,,,,,,,,,,,,,,,,,,,,,,,

    Reply
  9. rtnm

    (8) DJDUH, Не уверен что я правильно понял вопрос, но вот так он читает телефоны:

    Процедура УзелТелефонЗавершение(Узел) Экспорт
    КонтрагентОбъект = Узел.Родитель.ДопСвойства.КонтрагентОбъект;
    КонтрагентОбъект.Телефоны = КонтрагентОбъект.Телефоны + Узел.Значение + «;»;
    КонецПроцедуры
    
    Reply
  10. DAnry

    Мне тоже нравится. Плюсанул…

    Reply
  11. Evil Beaver

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

    Короче, сделан костыль и костыль отрекламирован. Теперь 1С-ник Вася вместо изучения XDTO будет лепить вот такие-вот обходные горы кода, вместо существующего, более автоматизированного и типобезопасного способа чтения XML.

    Технически решение красивое, пожалуй, но на прикладную ценность не тянет. Разумеется, это чисто мое скромное мнение, вы можете с ним не соглашаться.

    Reply
  12. Mogidin

    а можно воспользоваться Msxml2.DOMDocument

    ФайлXML = Новый COMОбъект(«MSXML2.DOMDocument.4.0»);

    и вперед

    http://msdn.microsoft.com/en-us/library/aa923288.aspx

    то же ЧтениеXML, но гораздо быстрее

    Reply
  13. aximo

    Кстати, Вы реально работали с большими XML-файлами? Дело в том, что при сохранение текста разметки на УФ больше 10 мб (по опыту) платформа 8.2 может выдавать ошибку сохранения файла.

    Reply
  14. rtnm

    (13) aximo, с большими XML-файлами вместе с УФ не работал

    Reply
  15. jobkostya1c8

    (12) Mogidin, там уже не к 1С относится, а общее описание стандарта DOM-технологии. Еще и на английском. Придется искать уже переработанное и на русском для 1С 8, но за идейку большой плюс.

    Для маленьких и средних технология «проглатывания» разом большого документа в какой-то объект в ОЗУ очень хорошо: и прочитали все сразу и разобрали, и, возможно, запрещенные символы самим объектом удалось обработать. Еще может и надежнее, раз прочитали весь объект то видим все взаимосвязанные ссылки по всему объекту, а вот при последовательном чтении только какую-то часть большого файла. Свыше 2 Гб XML вообще не эффективен. Тут уже и DOMDocument не поможет. Любой сервер загнется.

    Технологию описанную автором статьи, наверное, стоит применять если структура XML-файла простая (типа таблица ФИО, год рождения, адрес…), однотипная, без глубинных вложений, взаимосвязанных ссылок. Или если это остаток древней технологии старых решений.

    Если можно, то приведите примеры ссылок на уже готовые простые решения DOM чтения и обработки.

    Reply
  16. Evil Beaver

    (12) Mogidin, че, прям гораздо? А цифры есть?

    Reply
  17. DrZombi

    Автор, без обид, но отстой 🙂

    Сколько не писал обмены на XML… но самый лучший прирост мне дал обычный DBF файл 🙂

    …а XML — это файл для организации мелких обменов, которые и тем же DBF файлами будут работать куда приятней 🙂



    п.с. на DBF тоже можно организовывать древа, но накой оно надо 😉

    Reply
  18. rtnm

    (17) DrZombi, в следующий раз так и скажи заказчику: «Ваш XML отстой. Дайте мне DBF, мне с ним куда приятней работать» 🙂

    Reply
  19. jobkostya1c8

    Самое неприятное в формате DBF это длинна текстового поля (боюсь ошибиться) 254 символа и потом уже средствами 1С приходится склеивать строки или при выгрузке в DBF дробить на части. Причем сам формат DBF последних редакций может и поддерживать поле МЕМО (строка неограниченной длины), а вот в 1С объект XBase уже нет.

    Смысл использовать dbf если только сторонняя АИС типа paradox (foxPro, 1С 7.7 в файловом режиме) его генерирует для обмена (или загружают в нее) или это и есть внешний источник данных.

    Reply

Leave a Comment

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