В практике автоматизации часто встречается задача загрузки прайс-листов или отчетов, поступающих на электронную почту. Стандартный сценарий предполагает сохранение файла на диск и последующее его чтение. Однако возникают вопросы: можно ли оптимизировать процесс, читая данные напрямую из оперативной памяти (из потока), и как избежать ошибок при получении числовых значений. Рассмотрим эти задачи подробно, опираясь на опыт сообщества и технические особенности платформы 1С:Предприятие. Для комплексного переноса файлов различных форматов в базу 1С можно использовать обработку импорта данных из XLS, XLSX и ODS.
Обычно алгоритм работы с почтовыми вложениями выглядит так: получаем письмо -> извлекаем ДвоичныеДанные вложения -> сохраняем во временный файл -> открываем файл табличным документом или через COM-объект.
Однако автор темы задался вопросом: можно ли пропустить этап сохранения файла и прочитать ДвоичныеДанные напрямую? Это логично с точки зрения производительности и чистоты кода (не нужно следить за удалением временных файлов).
В платформе 1С существует возможность чтения табличных документов из потока. Метод Прочитать у объекта ТабличныйДокумент принимает на вход не только имя файла, но и объект типа Поток.
Разберем, как это реализовать. Когда мы получаем вложение из почты, мы имеем объект ДвоичныеДанные. Нам необходимо преобразовать его в поток, доступный для чтения.
// Примерный алгоритм получения потока из вложения
Вложение = Письмо.Вложения[0]; // Получаем первое вложение
ДвоичныеДанныеФайла = Вложение.Данные;
// Открываем поток для чтения из двоичных данных
ТекПоток = ДвоичныеДанныеФайла.ОткрытьПотокДляЧтения();
ТабДок = Новый ТабличныйДокумент;
// Попытка прочитать поток напрямую
// Важно: не все форматы Excel идеально читаются из потока в старых версиях платформы
Попытка
ТабДок.Прочитать(ТекПоток);
Исключение
Сообщить("Не удалось прочитать поток: " + ОписаниеОшибки());
КонецПопытки;
Важные нюансы форматов (XLS, XLSX, ODS):
ТабличныйДокумент.Прочитать. Для таких файлов классический метод сохранения во временный файл остается самым надежным.Если вы хотите явно указать формат файла при чтении из потока, используйте второй параметр метода:
// Явное указание, что мы ожидаем формат XLSX или ODS
ТабДок.Прочитать(ТекПоток, СпособЧтенияЗначенийТабличногоДокумента.Значение, ТипФайлаТабличногоДокумента.XLSX);
Если поток не читается (например, пришел старый .xls), лучшей практикой остается использование временных файлов. Это гарантирует, что драйверы или механизмы платформы смогут корректно обратиться к данным. Для программного чтения листов и переноса их содержимого в таблицу значений подойдёт обработка для загрузки файлов Excel и CSV.
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xls");
ДвоичныеДанныеФайла.Записать(ИмяВременногоФайла);
ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ИмяВременногоФайла);
// После обработки не забываем удалить мусор
УдалитьФайлы(ИмяВременногоФайла);
Вторая распространенная проблема, затронутая в обсуждении, звучит так: "В ячейке Excel написано 14,73, а в 1С прилетает ровно 14. Цифры обрезаются после запятой".
Давайте разберем причину. Объект ТабличныйДокумент (и ячейки Excel) имеет два разных свойства, которые новички часто путают:
14.73, но формат ячейки настроен как "Числовой, 0 знаков после запятой", пользователь видит "15" (или "14" в зависимости от округления отображения). Свойство Текст вернет именно эту строку.В приведенном в теме примере кода использовалось свойство Текст:
// ОШИБКА: Получение отформатированной строки вместо числа
Столбец = ТекущаяОбласть.Текст;
Если Excel-файл был сохранен с форматированием, скрывающим знаки после запятой, 1С при чтении в свойство Текст поместит именно обрезанный вариант. Чтобы получить точное число, необходимо обращаться к значению ячейки.
При работе с областью ячеек следует использовать свойство Значение или проверять СодержитЗначение.
Область = ТабДок.ПолучитьОбласть("R" + НомерСтроки + "C" + НомерКолонки);
// Правильное чтение
Если Область.СодержитЗначение Тогда
ТочноеЧисло = Область.Значение; // Здесь будет 14.73
Иначе
// Если значения нет, пробуем преобразовать текст,
// но помним про риски форматирования
ТочноеЧисло = Число(Область.Текст);
КонецЕсли;
Также обратите внимание на параметр метода Прочитать: СпособЧтенияЗначенийТабличногоДокумента. По умолчанию он может быть установлен в Текст, что заставляет платформу интерпретировать все как строки. Убедитесь, что используете режим Значение.
В ходе обсуждения возник вопрос: как после обработки файла программно пометить письмо как прочитанное на почтовом сервере?
Это зависит от протокола, по которому вы работаете с почтой (POP3 или IMAP). В современной разработке чаще используется IMAP, так как он позволяет управлять статусами писем.
При использовании объекта ИнтернетПочта:
Выбрать (или Connect при выборке заголовков) имеет параметры, отвечающие за режим чтения. Если вы хотите, чтобы письма сразу помечались прочитанными при скачивании, нужно смотреть документацию к методу конкретной версии платформы (часто это регулируется параметром ТолькоДляЧтения = Ложь).
Пример изменения статуса для протокола IMAP (доступно в новых версиях платформы через расширенные возможности работы с почтой или внешние компоненты, либо стандартными методами при выборке):
// Пример логики (синтаксис может отличаться в зависимости от версии платформы)
ПараметрыОтбора = Новый Структура;
// ... настройки отбора ...
// Важный параметр метода Выбрать (или аналогичного для IMAP)
// Третий параметр часто отвечает за пометку "Прочитано" (Seen)
Почта.Выбрать(Ложь, ПараметрыОтбора);
Также можно использовать метод УстановитьФлагиСообщений (если поддерживается текущим режимом работы), передав туда массив идентификаторов сообщений и флаг "Seen" (Прочитано).
Для эффективной обработки прайс-листов из почты рекомендуем:
Значение, а не Текст, чтобы избежать ошибок округления и форматирования.ИнтернетПочта.