Как прочитать Excel из вложения почты напрямую и решить проблему с точностью чисел?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике автоматизации часто встречается задача загрузки прайс-листов или отчетов, поступающих на электронную почту. Стандартный сценарий предполагает сохранение файла на диск и последующее его чтение. Однако возникают вопросы: можно ли оптимизировать процесс, читая данные напрямую из оперативной памяти (из потока), и как избежать ошибок при получении числовых значений. Рассмотрим эти задачи подробно, опираясь на опыт сообщества и технические особенности платформы 1С:Предприятие. Для комплексного переноса файлов различных форматов в базу 1С можно использовать обработку импорта данных из XLS, XLSX и ODS.

Задача 1: Чтение файла из вложения без сохранения на диск

Обычно алгоритм работы с почтовыми вложениями выглядит так: получаем письмо -> извлекаем ДвоичныеДанные вложения -> сохраняем во временный файл -> открываем файл табличным документом или через COM-объект.

Однако автор темы задался вопросом: можно ли пропустить этап сохранения файла и прочитать ДвоичныеДанные напрямую? Это логично с точки зрения производительности и чистоты кода (не нужно следить за удалением временных файлов).

Решение через Поток (Stream)

В платформе 1С существует возможность чтения табличных документов из потока. Метод Прочитать у объекта ТабличныйДокумент принимает на вход не только имя файла, но и объект типа Поток.

Разберем, как это реализовать. Когда мы получаем вложение из почты, мы имеем объект ДвоичныеДанные. Нам необходимо преобразовать его в поток, доступный для чтения.


// Примерный алгоритм получения потока из вложения
Вложение = Письмо.Вложения[0]; // Получаем первое вложение
ДвоичныеДанныеФайла = Вложение.Данные;

// Открываем поток для чтения из двоичных данных
ТекПоток = ДвоичныеДанныеФайла.ОткрытьПотокДляЧтения();

ТабДок = Новый ТабличныйДокумент;

// Попытка прочитать поток напрямую
// Важно: не все форматы Excel идеально читаются из потока в старых версиях платформы
Попытка
    ТабДок.Прочитать(ТекПоток); 
Исключение
    Сообщить("Не удалось прочитать поток: " + ОписаниеОшибки());
КонецПопытки;

Важные нюансы форматов (XLS, XLSX, ODS):

  1. Формат ODS и MXL: Платформа 1С «роднее» всего воспринимает свои форматы и открытый формат OpenOffice/LibreOffice (ODS). В обсуждении участники выяснили, что смена формата файла на ODS позволила без проблем прочитать документ из потока.
  2. Формат XLSX: В современных версиях платформы (8.3.6+) поддержка чтения XLSX из потока значительно улучшена. Вы можете явно указать тип файла при чтении.
  3. Формат XLS (старый Excel 97-2003): Чтение бинарного формата XLS напрямую из потока работает нестабильно или не поддерживается вовсе встроенным методом ТабличныйДокумент.Прочитать. Для таких файлов классический метод сохранения во временный файл остается самым надежным.

Если вы хотите явно указать формат файла при чтении из потока, используйте второй параметр метода:


// Явное указание, что мы ожидаем формат XLSX или ODS
ТабДок.Прочитать(ТекПоток, СпособЧтенияЗначенийТабличногоДокумента.Значение, ТипФайлаТабличногоДокумента.XLSX);

Альтернативный (классический) метод: Временный файл

Если поток не читается (например, пришел старый .xls), лучшей практикой остается использование временных файлов. Это гарантирует, что драйверы или механизмы платформы смогут корректно обратиться к данным. Для программного чтения листов и переноса их содержимого в таблицу значений подойдёт обработка для загрузки файлов Excel и CSV.


ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xls");
ДвоичныеДанныеФайла.Записать(ИмяВременногоФайла);

ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ИмяВременногоФайла);

// После обработки не забываем удалить мусор
УдалитьФайлы(ИмяВременногоФайла);

Задача 2: Проблема точности чисел (Текст vs Значение)

Вторая распространенная проблема, затронутая в обсуждении, звучит так: "В ячейке Excel написано 14,73, а в 1С прилетает ровно 14. Цифры обрезаются после запятой".

Давайте разберем причину. Объект ТабличныйДокумент (и ячейки Excel) имеет два разных свойства, которые новички часто путают:

  1. Текст (Text): Это строковое представление того, что вы видите на экране. Если в Excel ячейка имеет значение 14.73, но формат ячейки настроен как "Числовой, 0 знаков после запятой", пользователь видит "15" (или "14" в зависимости от округления отображения). Свойство Текст вернет именно эту строку.
  2. Значение (Value): Это реальные данные, хранящиеся в ячейке, без учета маски форматирования.

В приведенном в теме примере кода использовалось свойство Текст:


// ОШИБКА: Получение отформатированной строки вместо числа
Столбец = ТекущаяОбласть.Текст; 

Если Excel-файл был сохранен с форматированием, скрывающим знаки после запятой, 1С при чтении в свойство Текст поместит именно обрезанный вариант. Чтобы получить точное число, необходимо обращаться к значению ячейки.

Правильный способ получения данных

При работе с областью ячеек следует использовать свойство Значение или проверять СодержитЗначение.


Область = ТабДок.ПолучитьОбласть("R" + НомерСтроки + "C" + НомерКолонки);
// Правильное чтение
Если Область.СодержитЗначение Тогда
    ТочноеЧисло = Область.Значение; // Здесь будет 14.73
Иначе
    // Если значения нет, пробуем преобразовать текст, 
    // но помним про риски форматирования
    ТочноеЧисло = Число(Область.Текст); 
КонецЕсли;

Также обратите внимание на параметр метода Прочитать: СпособЧтенияЗначенийТабличногоДокумента. По умолчанию он может быть установлен в Текст, что заставляет платформу интерпретировать все как строки. Убедитесь, что используете режим Значение.

Задача 3: Пометить письмо как прочитанное

В ходе обсуждения возник вопрос: как после обработки файла программно пометить письмо как прочитанное на почтовом сервере?

Это зависит от протокола, по которому вы работаете с почтой (POP3 или IMAP). В современной разработке чаще используется IMAP, так как он позволяет управлять статусами писем.

При использовании объекта ИнтернетПочта:

  1. При получении писем: Метод Выбрать (или Connect при выборке заголовков) имеет параметры, отвечающие за режим чтения. Если вы хотите, чтобы письма сразу помечались прочитанными при скачивании, нужно смотреть документацию к методу конкретной версии платформы (часто это регулируется параметром ТолькоДляЧтения = Ложь).
  2. Явная установка флага (IMAP): Если вы уже скачали заголовки или письма, вы можете изменить их статус отдельно.

Пример изменения статуса для протокола IMAP (доступно в новых версиях платформы через расширенные возможности работы с почтой или внешние компоненты, либо стандартными методами при выборке):


// Пример логики (синтаксис может отличаться в зависимости от версии платформы)
ПараметрыОтбора = Новый Структура;
// ... настройки отбора ...

// Важный параметр метода Выбрать (или аналогичного для IMAP)
// Третий параметр часто отвечает за пометку "Прочитано" (Seen)
Почта.Выбрать(Ложь, ПараметрыОтбора); 

Также можно использовать метод УстановитьФлагиСообщений (если поддерживается текущим режимом работы), передав туда массив идентификаторов сообщений и флаг "Seen" (Прочитано).

Итог

Для эффективной обработки прайс-листов из почты рекомендуем:

  1. Пробовать читать вложение напрямую из потока, если формат файла — ODS или современный XLSX. Это экономит ресурсы сервера.
  2. Для старых форматов XLS надежнее использовать временный файл.
  3. При чтении данных из ячеек всегда обращаться к свойству Значение, а не Текст, чтобы избежать ошибок округления и форматирования.
  4. Для управления статусом писем (прочитано/не прочитано) использовать возможности протокола IMAP и соответствующие методы объекта ИнтернетПочта.
← На главную