Изображения из xlsx в справочник 1С: чтение архива, привязка к строкам и запись в базу
Содержание
Загрузить картинку из Excel в 1С надёжнее всего так: открыть файл .xlsx как ZIP-архив, взять изображения из папки xl/media, а номер строки для каждого рисунка прочитать из xl/drawings/drawing1.xml. Значения ячеек (артикул, наименование) читает обычный ТабличныйДокумент.Прочитать(). По артикулу находим элемент справочника и пишем картинку в реквизит типа ХранилищеЗначения или в присоединённые файлы.
Частое заблуждение — что достаточно прочитать книгу в табличный документ, и картинки «приедут» вместе с ячейками. Значения действительно приедут. А вот рисунки в коллекции Рисунки табличного документа появляются не всегда и без удобной привязки к строке. Поэтому для пошаговой загрузки изображений в справочник ниже используется разбор архива — он не зависит ни от Excel на компьютере, ни от того, как платформа распознала графику.
Как подготовить Excel-файл, чтобы картинки нашлись
Половина проблем с загрузкой решается ещё до написания кода. Договоритесь с тем, кто готовит файл, о простых правилах.
📌 Формат и структура:
- только
.xlsx— старый.xlsне является ZIP-архивом, метод с разбором не сработает; - одна строка — один товар, первая строка — заголовки;
- в первой колонке ключ для поиска: артикул или код, без пробелов по краям;
- картинка лежит внутри ячейки своей строки и не залезает верхним краем на строку выше;
- все данные на первом листе, без объединённых ячеек в шапке.
📌 Сами изображения:
- вставлены через
Вставка → Рисунки, а не как фон или примечание; - сжаты: в Excel выделите любой рисунок и на вкладке работы с рисунками выберите «Сжать рисунки», снимите галку «Применить только к этому рисунку»;
- форматы PNG или JPEG. EMF и WMF 1С прочитает, но на формах они часто выглядят хуже.
В новых версиях Excel 365 есть режим «Поместить в ячейку». Такая картинка хранится не в xl/drawings, а в отдельном хранилище расширенных данных книги. Разбор из этой статьи её не увидит. Перед загрузкой выделите такие ячейки и через контекстное меню переведите рисунок в режим «Поместить поверх ячеек».
💡 Если картинок сотни, проще вообще не вставлять их в Excel. Положите файлы в папку, а в колонку B впишите имя файла: art-10025.jpg. Загрузка сведётся к Новый ДвоичныеДанные(ПутьКПапке + ИмяФайла) — без разбора XML.
Шаблон файла и форма обработки для загрузки изображений
Чтобы пользователи не присылали файлы «кто во что горазд», положите шаблон прямо в обработку. Создайте в конфигураторе внешнюю обработку
и добавьте макет типа «Двоичные данные» с именем Шаблон. В него загрузите пустой .xlsx с правильными колонками.
Кнопка «Скачать шаблон» на форме отдаёт этот макет пользователю:
&НаСервере
Функция АдресШаблона()
Обработка = РеквизитФормыВЗначение("Объект");
Возврат ПоместитьВоВременноеХранилище(Обработка.ПолучитьМакет("Шаблон"), УникальныйИдентификатор);
КонецФункции
&НаКлиенте
Процедура СкачатьШаблон(Команда)
ПолучитьФайл(АдресШаблона(), "Шаблон загрузки картинок.xlsx", Истина);
КонецПроцедуры
На саму форму добавьте:
- Реквизит
АдресФайла(строка) — сюда попадёт адрес загруженной книги во временном хранилище. - Команду «Выбрать файл», которая вызывает
НачатьПомещениеФайла()и сохраняет полученный адрес. - Таблицу значений
Результатс колонками «Строка», «Артикул», «Статус» — чтобы после загрузки было видно, что не нашлось. - По желанию — поле картинки для предпросмотра: реквизит-строка с адресом во временном хранилище, элемент формы «Поле картинки».
Файл передаём на сервер как двоичные данные. На сервере записываем их во временный файл — оба метода чтения ниже работают с путём на диске.
Чем 1С читает xlsx: табличный документ, ZIP-архив и COM
У разработчика три инструмента. Каждый закрывает свою часть задачи.
Табличный документ. ТабличныйДокумент.Прочитать(ИмяФайла) открывает .xlsx в актуальных версиях платформы 8.3 без установленного Excel. Работает на сервере и в Linux. Для текстовых колонок — идеально.
ZIP-архив. Формат .xlsx — это обычный ZIP с XML-файлами внутри. В нём есть всё, что нужно:
| Файл внутри архива | Что в нём |
|---|---|
xl/media/image1.png … | сами картинки в исходном формате |
xl/drawings/drawing1.xml | якоря рисунков: строка и колонка ячейки |
xl/drawings/_rels/drawing1.xml.rels | связь «идентификатор рисунка → файл в media» |
xl/worksheets/_rels/sheet1.xml.rels | какой drawing относится к какому листу |
Объект ЧтениеZipФайла распаковывает архив, ПостроительDOM читает XML. Excel не нужен.
COM. Через Новый COMОбъект("Excel.Application") можно обойти коллекцию Shapes листа и узнать строку рисунка через TopLeftCell.Row. Но выгрузить саму картинку COM не умеет напрямую — подробнее в разделе сравнения.
Рабочая связка: значения ячеек — табличным документом, картинки — из архива.
Код загрузки: картинки из Excel по строкам через ZIP и XML
Функция ниже принимает путь к .xlsx и возвращает соответствие «номер строки → двоичные данные картинки». Номер строки — как в Excel, начиная с 1.
&НаСервере
Функция КартинкиПоСтрокам(ИмяФайлаXlsx)
NSxdr = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing";
NSa = "http://schemas.openxmlformats.org/drawingml/2006/main";
NSr = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
NSrel = "http://schemas.openxmlformats.org/package/2006/relationships";
Разд = ПолучитьРазделительПути();
Каталог = КаталогВременныхФайлов() + Строка(Новый УникальныйИдентификатор) + Разд;
СоздатьКаталог(Каталог);
Zip = Новый ЧтениеZipФайла(ИмяФайлаXlsx);
Zip.ИзвлечьВсе(Каталог, РежимВосстановленияПутейФайловZIP.Восстанавливать);
Zip.Закрыть();
Результат = Новый Соответствие;
ПапкаDrawings = Каталог + "xl" + Разд + "drawings" + Разд;
ФайлРисунков = ПапкаDrawings + "drawing1.xml";
ФайлСвязей = ПапкаDrawings + "_rels" + Разд + "drawing1.xml.rels";
Если Не (Новый Файл(ФайлРисунков)).Существует() Тогда
УдалитьФайлы(Каталог);
Возврат Результат; // в книге нет рисунков поверх ячеек
КонецЕсли;
// 1. Связи: rId -> имя файла в xl/media
Связи = Новый Соответствие;
СписокСвязей = ПрочитатьDOM(ФайлСвязей).ПолучитьЭлементыПоИмени(NSrel, "Relationship");
Для Инд = 0 По СписокСвязей.Количество() - 1 Цикл
Эл = СписокСвязей.Элемент(Инд);
Цель = Эл.ПолучитьАтрибут("Target"); // например ../media/image3.png
Связи.Вставить(Эл.ПолучитьАтрибут("Id"), СтрЗаменить(Цель, "../media/", ""));
КонецЦикла;
// 2. Якоря: строка ячейки и ссылка на картинку
ДокРисунков = ПрочитатьDOM(ФайлРисунков);
Для Каждого ИмяЯкоря Из СтрРазделить("twoCellAnchor,oneCellAnchor", ",") Цикл
Якоря = ДокРисунков.ПолучитьЭлементыПоИмени(NSxdr, ИмяЯкоря);
Для Инд = 0 По Якоря.Количество() - 1 Цикл
Якорь = Якоря.Элемент(Инд);
Blip = Якорь.ПолучитьЭлементыПоИмени(NSa, "blip");
Если Blip.Количество() = 0 Тогда
Продолжить; // фигура или надпись, а не картинка
КонецЕсли;
ИдСвязи = Blip.Элемент(0).ПолучитьАтрибутNS(NSr, "embed");
ИмяМедиа = Связи.Получить(ИдСвязи);
Если ИмяМедиа = Неопределено Тогда
Продолжить;
КонецЕсли;
ОтКуда = Якорь.ПолучитьЭлементыПоИмени(NSxdr, "from").Элемент(0);
СтрокаXML = ОтКуда.ПолучитьЭлементыПоИмени(NSxdr, "row").Элемент(0).ТекстовоеСодержимое;
НомерСтроки = Число(СтрокаXML) + 1; // в XML строки считаются с нуля
ПутьКартинки = Каталог + "xl" + Разд + "media" + Разд + ИмяМедиа;
Результат.Вставить(НомерСтроки, Новый ДвоичныеДанные(ПутьКартинки));
КонецЦикла;
КонецЦикла;
УдалитьФайлы(Каталог);
Возврат Результат;
КонецФункции
&НаСервере
Функция ПрочитатьDOM(ИмяФайла)
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьФайл(ИмяФайла);
Построитель = Новый ПостроительDOM;
Документ = Построитель.Прочитать(Чтение);
Чтение.Закрыть();
Возврат Документ;
КонецФункции
Три момента, на которых чаще всего ошибаются:
- Строки с нуля. В
drawing1.xmlпервая строка листа — это0. Отсюда+ 1. Без него все картинки съедут на строку вверх, и шапка «получит» фото первого товара. - Два вида якорей. Картинка, которая перемещается вместе с ячейками, записана как
twoCellAnchor, «плавающая» — какoneCellAnchor. Обходим оба. - Номер drawing. У первого листа почти всегда
drawing1.xml. Если в книге несколько листов с рисунками, точное соответствие лежит вxl/worksheets/_rels/sheet1.xml.rels.
Если в одной строке две картинки, в соответствии останется последняя. Нужны все — храните в значении массив двоичных данных.
Запись в реквизит ХранениеКартинки и работа с двоичными данными
Теперь соединяем картинки с ячейками. Пример для собственной конфигурации, где у справочника Номенклатура есть реквизит Артикул и реквизит ХранениеКартинки типа ХранилищеЗначения:
&НаСервере
Процедура ЗагрузитьНаСервере()
ИмяФайла = ПолучитьИмяВременногоФайла("xlsx");
ПолучитьИзВременногоХранилища(АдресФайла).Записать(ИмяФайла);
ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ИмяФайла);
Картинки = КартинкиПоСтрокам(ИмяФайла);
УдалитьФайлы(ИмяФайла);
Результат.Очистить();
Для НомерСтроки = 2 По ТабДок.ВысотаТаблицы Цикл
Артикул = СокрЛП(ТабДок.Область(НомерСтроки, 1, НомерСтроки, 1).Текст);
ДанныеКартинки = Картинки.Получить(НомерСтроки);
Если ПустаяСтрока(Артикул) Или ДанныеКартинки = Неопределено Тогда
Продолжить;
КонецЕсли;
НоваяСтрока = Результат.Добавить();
НоваяСтрока.Строка = НомерСтроки;
НоваяСтрока.Артикул = Артикул;
Ссылка = Справочники.Номенклатура.НайтиПоРеквизиту("Артикул", Артикул);
Если Ссылка.Пустая() Тогда
НоваяСтрока.Статус = "Не найден артикул";
Продолжить;
КонецЕсли;
ТоварОбъект = Ссылка.ПолучитьОбъект();
ТоварОбъект.ХранениеКартинки = Новый ХранилищеЗначения(ДанныеКартинки, Новый СжатиеДанных(9));
ТоварОбъект.Записать();
НоваяСтрока.Статус = "Загружено";
КонецЦикла;
КонецПроцедуры
Что важно про двоичные данные:
- 🗜️
Новый СжатиеДанных(9)— максимальное сжатие. PNG и JPEG уже сжаты, выигрыш небольшой, но для BMP и EMF заметный. - 🔙 Обратно картинку достают так:
Объект.ХранениеКартинки.Получить()вернётДвоичныеДанные. Для показа на форме поместите их во временное хранилище и отдайте адрес полю картинки. - 🖼️ Нужен объект
Картинка(например, для печатной формы) —Новый Картинка(ДанныеКартинки).
Типовые конфигурации. В БП 3.0, УТ 11 и других решениях на БСП картинка номенклатуры хранится не в реквизите самого справочника, а как присоединённый файл; основное изображение указывается в карточке. Писать туда напрямую в таблицы не стоит: используйте программный интерфейс подсистемы «Работа с файлами» из БСП, а основной файл назначайте через реквизит карточки. Загрузку в рабочую базу оформляйте внешней обработкой, без снятия конфигурации с поддержки.
Перед первым запуском на рабочей базе сделайте выгрузку .dt или копию. Обработка массово перезаписывает элементы справочника, а старые картинки будут заменены без возможности отката.
Табличный документ, ZIP или COM: какой способ выбрать
Код через COM выглядит так — для сравнения:
Excel = Новый COMОбъект("Excel.Application");
Книга = Excel.Workbooks.Open(ИмяФайла);
Лист = Книга.Worksheets(1);
Для Инд = 1 По Лист.Shapes.Count Цикл
Фигура = Лист.Shapes.Item(Инд);
НомерСтроки = Фигура.TopLeftCell.Row; // здесь уже с единицы
// выгрузка: Фигура.CopyPicture(), вставка в временную диаграмму,
// затем Chart.Export(ПутьКФайлу) и удаление диаграммы
КонецЦикла;
Книга.Close(Ложь);
Excel.Quit();
Сама картинка через COM добывается обходным путём: копирование в буфер обмена и экспорт временной диаграммы. На сервере без интерактивного сеанса буфер обмена обычно недоступен, и способ падает.
| Способ | Когда подходит | Риски и ограничения |
|---|---|---|
| Табличный документ | только значения ячеек, любой сервер | рисунки ненадёжны, нет привязки к строке |
| ZIP + XML | картинки поверх ячеек, сервер Windows или Linux | не видит режим «Поместить в ячейку» и .xls |
| COM Excel | старый .xls, разовая загрузка на машине с Excel | нужен установленный Excel, только Windows, буфер обмена, потеря качества при экспорте |
| Папка с файлами + имя в колонке | сотни и тысячи картинок | нужно отдельно передать папку |
Для регулярной загрузки от поставщика выбирайте ZIP. COM оставьте для разовых задач со старыми файлами — а лучше попросите пересохранить их в .xlsx.
Ошибки при загрузке изображений и скорость на больших файлах
Чтобы загрузка тысяч строк не растягивалась на час:
- Не читайте объект зря. Сравнивайте хеш новой картинки со старой и пропускайте одинаковые. Хеш даёт объект
ХешированиеДанныхс функциейХешФункция.MD5. - Ограничьте размер. Проверяйте
ДанныеКартинки.Размер()и откладывайте в отчёт всё, что больше, например, 2 МБ. - Пишите порциями. Оборачивайте по 100-200 записей в
НачатьТранзакцию()/ЗафиксироватьТранзакцию()с обработкой исключения. Одна гигантская транзакция держит блокировки на справочнике и мешает пользователям. - Кешируйте поиск. Для 5 000 строк один запрос «артикул → ссылка» заранее быстрее, чем 5 000 вызовов
НайтиПоРеквизиту. - Убирайте за собой. Временный каталог с распакованным архивом удаляйте и при ошибке — оберните разбор в
Попытка … Исключение.
Частые вопросы
Можно ли загрузить картинки из Excel без программирования?
Будет ли код работать в файловой базе?
Работает ли разбор на сервере 1С под Linux?
ЧтениеZipФайла и ПостроительDOM от операционной системы не зависят. Разделитель пути берите через ПолучитьРазделительПути(), а не пишите обратный слеш.Как загрузить картинки из Google Таблиц?
.xlsx. Картинки, вставленные функцией IMAGE, в архив не попадают — для них выгрузите ссылки и скачивайте файлы по URL отдельно.