Хранение рисунков в отдельной базе MSSQL с помощью ADODB и Base64Строка




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

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

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

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

40 Comments

  1. ПСВ

    use tempdb

    CREATE TABLE myTable(FileName int,

    Document varbinary(max))

    GO

    insert myTable(Filename,Document)

    select 1,null

    GO

    UPDATE myTable

    SET Document = (

    SELECT *

    FROM OPENROWSET(BULK ‘D:картинка14.jpg’, SINGLE_BLOB)AS x )

    WHERE FileName = 1;

    GO

    select * from mytable

    Reply
  2. Ivon

    (1). Интересное решение. Не знал. А 1С как воспримет банные из varbinary?

    Reply
  3. a_titeev

    ИМХО, все-таки правильнее было бы использовать не nvarchar-поле, а image…

    кстати тогда получаем еще одно преимущество — можно хранить, например, различные документы (doc, .xls, .ppt, .txt, .html, .pdf,…), а для них можно применить индексирование с помощью фильтров и использовать full-text search для поиска по содержимому документов…

    хранение в nvarchar этого кажись не позволит, ибо сказано было в документации: «Formatted text strings, such as Microsoft® Word™ document files or HTML files, cannot be stored in character string or Unicode columns because many of the bytes in these files contain data structures that do not form valid characters.»

    Reply
  4. Ivon

    (3). Я использую Base64Строка(Картинка.ПолучитьДвоичныеДанные()). А там можно любые двоичные данные в виде строки представить.

    Reply
  5. a_titeev

    (4). Так это и понятно, но лучше бы как-нибудь без Base64… Искусственно размер увеличивается, во первых. Во вторых, если все таки хранить что нибудь типа документов, как я писал ранее, теряется возможность использования полнотекстового поиска по содержимому внутри сиквельной таблицы.

    Можно попробовать через временные файлы… Типа так: StoredProc заводишь, которая содержимое image сливает в файл с заданным именем, а ты этот файл ДвоичнымиДанными…, и делаешь что нада… или же сразу картинку на основании файла сделать можно, если это картинка…

    подобным образом сделал как то хранение документов, которые потом были доступны в 1с и одновременно с этим на сайтике… правда проект развития не получил, и все наработки непонятно где… скорее всего их и нет уже…

    но вообще, для простых вещей конечно и так достаточно…

    Reply
  6. Ivon

    (5). Надо будет попробовать. Просто с Base64 вроде как понятнее.

    Reply
  7. Ivon

    Попробовал ложить в двоичном виде — ложить получилось, а вот с чтением проблемы, конструкция

    Новый Картинка(RecordSet.Fields(0).Value)

    выдает ошибку

    Ошибка при вызове конструктора (Картинка): Несоответствие типов (параметр номер ‘1’)

    Как побороть — понятия не имею. Если кто-то знает (без сохранения в промежуточный файл), просьба отпостить. Пока вижу единственный рабочий вариант с Base64Строка.

    Reply
  8. Широкий
  9. a_titeev

    (8). Да-да. Байка про «неиспользование» типа image в будущих версиях есть и я это видел… действительно рекомендуется varbinary(max)… но в том числе и по личному опыту также знаю о различии в скоростях при работе с image и varbinary(max)… тынц

    в принципе в 2008 есть уже и varbinary(max) filestream — можно поюзать его… вроде как это уже именно для подобных целей и будет оптимально с точки зрения производительности. сам не пробовал, руки не доходили, попробую как нибудь позже…

    (7). Мало того, скорее всего то что залилось в базу — уже не картинка 🙁 …

    Reply
  10. Широкий
  11. Ivon

    (9). То есть на текущий момент лучше Base64Строка никто ничего работающего предложить не может…

    Reply
  12. a_titeev

    ща, отобьюсь он назойливых… поеду домой, попробую сырцы поискать… sql какой? может правда на filestream переделать?

    Reply
  13. Ivon

    (9). То есть на текущий момент лучше Base64Строка никто ничего работающего предложить не может…

    Можно, конечно использовать Stream, но здесь мы упираемся в необходимость сохранения файла на диск. В принципе, каждый из вариантов имеет свои плюсы и минусы, в мое случае плюс — это отсутствие необходимости вначале писать файл на диск, а минус — картинка занимает больше места, потому что вначале перекодируется с помощью Base64Строка.

    Вариант с varbinary в данном случае полностью противоположный моему: необходимость сохранения картинки на диск перед тем, как ее прочитать в поле картинки, но картинка в базе будет занимать меньше.

    Reply
  14. a_titeev

    (13). да никуда мы не упираемся… разум коллективный. эффект синергии… придумать можно чё нибудь…

    Reply
  15. _Z1

    Хранить файл в бд конечно хорошо но есть одно но УРБД.

    Я считаю что можно воспользоваться моей компонентой по хранению

    бинарных файлов в строке. Это лучше чем Base64Строка

    компонента и обсуждение здесь

    Преобразование картинки в строку

    Reply
  16. Ivon

    (16). Немного не по теме. Здесь обсуждается не вытягивание картинки из базы 1С в стороннее приложение, а сохранение изображений в отдельной MSSQL базе непосредственно из 1С и чтение этих изображений непосредственно в 1С.

    Reply
  17. a_titeev

    (13). н-да… нашел таки сырцы… ну и бред же я писал года полтора назад… 😉 шутка…

    да все, естественно через файлы… отличие только в том что через OPENROWSET заливались сразу все доки в папке… а для чтения тот же стрим и в файл… стрим нужен был видимо для того чтоб на локальной машине сохранять…

    но меня оправдывает то, что работа была именно с документами, а не с картинками, а там точно вариантов нет — только сохранение в темпарь… итак, дело в том, что тип COMSafeArray который ты получаешь на выходе с RecordSet.Fields(«…»).Value нада в виде типа Картинка получить… напрашивается что то типа ВК слепить по этому поводу… конечно если существует возможность тип Картинка или ДвоичныеДанные из нее вернуть… можно ли, не знаю…

    (15). наверное от специфики проекта зависит… в конце концов РБД не везде. да и дело то не только в хранении, а прежде всего в доступе к внешней базе для получения двоичных данных без промежуточных поползновений по жесткому диску… это может быть просто база внешнего приложения, вполне независимого от1с… строка — хорошо, но недостаточно для меня было, т.к. нужно было и документы не испортить (которые в отдельной системе использовались) и в 1с их как то поиметь…

    Reply
  18. Ivon

    (18). Согласен, если нужно использовать эти файлы не только в 1С, тогда только в бинарном виде сохранять. У меня случай проще, мне нужно использовать файлы только в 1С 8, поэтому Base64 выглядит привлекательнее, не нужно пользоваться временными файлами.

    Reply
  19. san4o

    Для решения задачи сохранения фотографий во внешней базе данных есть внешняя компонента «Фотоархив». Делалась для работы с фотографиями сотрудников в проекте учета рабочего времени. Работает под 7.7 и 8.1.

    База данных — SQL или файл Access (*.mdb). Можно использовать для сохранения любой информации, не только файлы с картинками. Например *.mp3 или *.doc. ВК сама создает базу данных в SQL и таблицу для сохранения. Можно задавать имя таблицы. Таблиц может быть сколько угодно.

    Объект сохраняется и извлекается из базы по произвольно сформированному ID. В комплекте поставки пример привязки фотографий по внутреннему ID объекта в 1С.

    Есть демо-версия http://www.wbp-sar.ru/joomla/index.php?option=com_content&view=article&id=20:-qq&catid=12:2009-10-08-11-25-36&Itemid=37

    Reply
  20. Ivon

    (20). 1) Сайт то-ли не работает полностью, то-ли о нем забыли.

    2) Похоже ВК платная, тогда зачем платить деньги, если мой работающий пример бесплатный и тоже позволяет хранить любой файл.

    3) Любая ВК требует регистрации ее с правами админа. Мой пример не использует ВК, поэтому будет работать сразу.

    Reply
  21. alex_4x

    А будет он работать с базой на postgres ?

    Reply
  22. Ivon

    (22). Именно данная обработка нет, но если в ней переделать строку подключения и строки запросов под postgres, то, я уверен, оно заработает.

    Reply
  23. Trakt0risT

    Всё бы не чего, но картинку большого размера не получится преобразовать в Строку (Base64Строка ) что делать? ((( есть выход?

    Reply
  24. Ivon

    (24). Большого — это какого? Я засовывал фотки 8-10 мегапикселей.

    Reply
  25. Trakt0risT

    Размером более 50 мегабайт!

    Reply
  26. Ivon

    (26). Может просто долго пишется… Или ошибку выдает?

    Reply
  27. Trakt0risT

    Именно ошибку выдает! говорить что слишком большой размер. приэтом пользовался всеми возможными методами преобразования в строку. не помогает! (

    решил проблему следующим методом:

    Stream=Новый COMОбъект(«ADODB.Stream»);

    Stream.Type=1;

    Stream.Open();

    Stream.LoadFromFile(FileData1);

    Reply
  28. legalas84

    Спасибо огромное. помогла обработка!

    Reply
  29. yalo

    Использовала данную обработку как основу для своей обработки. Очень полезная вещь, помогла очень. Спасибо!

    Reply
  30. Ivon

    (29), (30). У меня в профиле есть статья по хранению рисунков в базе без преобразования в Base64Строка. Работает быстрее и места в БД рисунки занимают меньше.

    Reply
  31. KroVladS

    (28) Trakt0risT,

    Можно по подробней, Вы строку передаёте потоком?

    (22) alex_4x, (25)

    Пытаюсь адаптировать для PostgreSQL. Преоброзование в Base64Строка файлов 8МБ проходит нормально, но при выполнении КомандаSQL.Execute(); либо ругаеться на надостаток памяти либо валиться платформа.

    Reply
  32. Ivon

    Вообще с Postgre очень странно работает. У меня, например, не работает выгрузка базы по расписанию. 1С просто вываливается с критической ошибкой. С MSSQL все нормально. Да и перестал я использовать 1С-ное поле картинки. Написал свою компоненту на C#, она и ложит рисунки в базу и отображает.

    Reply
  33. KroVladS

    Пытался адаптировать под PostgreSQL несколько вариантов файловых хранилишь которые нашёл на infostart.ru всё закончилось неудачай. Хранить через Base64Строка единственное что удалось заставить работать и то толко для небольших файлов 🙁

    Reply
  34. Alexander78

    а если тип данных в БД MySql Blob можно напрямую прочитать из 1С (без временного файла)?

    Reply
  35. Invaa

    Раз речь идет о MS SQL, то почему просто не пользоваться штатным механизмом файловых групп? И не нужно никакие сторонние базы создавать. Хочешь хоть каждый справочник или документ на отдельной дисковой подсистеме храни…

    Reply
  36. Ivon

    (36) Это Вы про какой штатный механизм упомянули? Что-то никто из моих коллег не в курсе…

    Reply
  37. Silenser

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

    Reply
  38. Ivon

    (38) Silenser.

    Я понял. Вопрос был как раз в том, чтобы хранить картинки именно в отдельной базе. Кроме того, хранение в 1С-овской базе занимает, похоже, столько же места, сколько и в отдельной базе MSSQL с использованием Base64Строка, как описано в этой строке, поэтому именно этот механизм в моей конфе не используется, а используется вот этот http://infostart.ru/public/77329/

    Reply
  39. Ivon

    Можно немного подправить код на SQL и саму обработку, следующим образом: создать на SQL хранимую процедуру, которая будет принимать Base64 и перекодировать его обратно в binary и ложить в базу уже двоичные данные, а так же вьюшку, которая будет делать обратное преобразование. Кстати, вьюшку можно прицепить, как внешний источник данных и впоследствии данные выбирать простым запросом 1С. В коде в 1С соответственно поменять вызов Insert на вызов хранимой процедуры, а вместо select использовать либо вьюшку в запросе либо вьюшку, как связанный источник данных.

    Reply

Leave a Comment

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