Передача файла с клиента на сервер и с сервера на клиент. Управляемые формы




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

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

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

<?php // Полная загрузка сервисных книжек, создан 2026-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='\

20 Comments

  1. Dream_kz

    Очередной велосипед из серии: «не нашел, сделал свой».

    https://infostart.ru/public/584472/

    https://infostart.ru/public/669204/

    https://infostart.ru/public/409865/

    https://infostart.ru/public/812886/

    ну и т.д.

    Reply ↓
  2. androgin

    какой треш!!!

    Это ж надо так размазать обычное получение файла ))

    Reply ↓
  3. milkers

    (1) Я нашел очень много на эту тему, но код меня не устроил. Я уже много лет пользуюсь инфостартом как репозиторием, чтобы можно было быстро как из кубиков собирать код. Из тех кубиков которые были слово «счастье» никак не складывалось. Проще потрать ещё час и все задокументировать, чем потом снова тратить полдня.

    Reply ↓
  4. milkers

    (3) Если Вы считаете, что здесь много лишнего, то буду только рад Вашим подсказкам.

    Reply ↓
  5. Diagnostiks

    Простую передачу файла на сервер и обратно превратили прям в «никогда такого не было, и вот опять». Очередной велосипед.

    Reply ↓
  6. milkers

    (6) Я бы и сам был бы рад сократить код. К сожалению, нужно учитывать кучу вещей, таких как асинхронное взаимодействие, удаление временных файлов за собой, возможность сохранения имени файла в настройках и т.д. Плюс желательно сделать его легко переносимым из одной обработки в другую, ну и по возможности читаемым. Делал для себя, чтобы разобраться с этим один раз и больше не тратить времени. Еще один строительный кубик в коллекцию, чтобы быстрее писать код. Если понравится кому-то еще я только за.

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

    Reply ↓
  7. Xershi

    В вашей статье не учитывается работа на веб клиенте, у меня в заготовках это предусмотрено, так что подумайте над универсальностью!

    Reply ↓
  8. Diagnostiks
    Делал для себя, чтобы разобраться с этим один раз и больше не тратить времени

    Странно, всегда думал, что программисты это не «тупой» народ (скопировал, вставил), а творческий, да и что там писать, экономия времени минут 5-10 максимум.

    Reply ↓
  9. milkers

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

    Reply ↓
  10. Diagnostiks

    Отлично. Но тогда все Ваши «шаблоны» кода надо где-то централизовано хранить. А можно сделать проще — шаблоны текста сделать уж тогда.

    Reply ↓
  11. androgin

    (7) НачатьПомещениеФайлов — тут и асинхрон и временное хранилище и имена файлов и даже можно открыть с помощью диалога и фильтрами расширений

    Reply ↓
  12. kadild

    Используйте НачатьПомещениеФайлов и читайте напрямую из двоичных данных без создания временных файлов (8.3.9).

    Функционал в пару строк у вас превратился в лапшу с кучей процедур.

    В целом хорошо, что делитесь информацией.

    Reply ↓
  13. kadild

    (9) Программисты не «тупой», поэтому собирают проект из заготовок. Сидеть творить над каждым функционалом недопустимо дорого, особенно во франчази.

    Reply ↓
  14. milkers

    (13) Разговор был бы предметнее, если бы вы привели рабочий вариант кода. Если он окажется лучше, я только рад буду использовать Ваш вариант.

    Reply ↓
  15. milkers

    (0) Более укороченный вариант кода, спасибо kadild , через НачатьПомещениеФайлов

    Reply ↓
  16. МимохожийОднако

    (13) Сбрось сюда примерчик

    Reply ↓
  17. shard

    хочу дополнить: при чтении табличного документа из файлов xls, xlsx, ods при получении имени временного файла указывать расширение обязательно, иначе будет «Ошибка при выполнении файловой операции»

    &НаКлиенте
    Процедура ВыполнитьКоманду(ИдентификаторКоманды, ОбъектыНазначенияМассив) Экспорт
    ДиалогФыбораФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
    
    ДиалогФыбораФайла.Фильтр                      = «Файл Excel (*.xls, *.xlsx)|*.xls?»;
    ДиалогФыбораФайла.Заголовок                   = «Выберите файл для загрузки данных»;
    ДиалогФыбораФайла.ПредварительныйПросмотр     = Ложь;
    ДиалогФыбораФайла.ИндексФильтра               = 0;
    ДиалогФыбораФайла.ПроверятьСуществованиеФайла = Истина;
    
    НачатьПомещениеФайла(Новый ОписаниеОповещения(«ВыполнитьЗагрузку»,ЭтаФорма),,ДиалогФыбораФайла,истина,УникальныйИдентификатор);
    КонецПроцедуры
    
    &НаКлиенте
    Процедура ВыполнитьЗагрузку(Результат,Адрес,ВыбранноеИмяФайла,ДополнительныеПараметры) Экспорт
    Если Не Результат Тогда
    Возврат;
    КонецЕсли;
    
     Расширение=СтрЗаменить(ВыбранноеИмяФайла,».»,Символы.ПС);
    Расширение=СтрПолучитьСтроку(Расширение,СтрЧислоСтрок(Расширение));
    ЗагрузитьНаСервере(Адрес,Расширение);
    //Сообщить(«Завершили»);
    КонецПроцедуры
    
    &НаСервереБезКонтекста
    Процедура ЗагрузитьНаСервере(Знач Адрес,Расширение)
    ФайлВременногоХранилища=ПолучитьИзВременногоХранилища(Адрес);
    ИмяФайла=ПолучитьИмяВременногоФайла(Расширение);
    ФайлВременногоХранилища.Записать(ИмяФайла);
    УдалитьИзВременногоХранилища(Адрес);
    MXL=Новый ТабличныйДокумент;
    MXL.Прочитать(ИмяФайла);
    УдалитьФайлы(ИмяФайла);
    КонецПроцедуры

    Показать

    Reply ↓
  18. makfromkz

    (15) Присоединяюсь , (13) мог бы показать кошерный код.

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

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

    Reply ↓
  19. gubanoff

    (0) помогло, хороший пример для быстрого старта!

    Reply ↓

Leave a Comment

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