При автоматизации бизнес-процессов часто возникает задача программной отправки печатных форм (счетов, актов, накладных) по электронной почте. Стандартный подход с сохранением файла на диск и последующим его прикреплением к письму часто приводит к засорению сервера временными файлами или возникновению ошибок доступа («Файл занят другим процессом»). В этой статье мы подробно разберем, как решить эту задачу максимально элегантно, используя современные возможности платформы 1С:Предприятие 8.3.
Начиная с версии платформы 8.3.10, объект ИнтернетПочтовоеВложение позволяет добавлять данные напрямую из оперативной памяти, используя объект ДвоичныеДанные. Это «золотой стандарт», который полностью исключает работу с файловой системой и снимает все вопросы о правах доступа к папкам или блокировках файлов.
Рассмотрим по шагам, как реализовать этот алгоритм:
ТабличныйДокумент.Проанализируем пример кода:
// 1. Получаем печатную форму (например, из менеджера документа)
ТабДок = Документы.СчетНаОплатуПокупателю.ПечатьСчета(СсылкаНаОбъект);
// 2. Преобразуем табличный документ в двоичные данные PDF напрямую в памяти
ДанныеPDF = ТабДок.ПолучитьДвоичныеДанные(ТипФайлаТабличногоДокумента.PDF);
// 3. Создаем почтовое сообщение
Сообщение = Новый ИнтернетПочтовоеСообщение;
Сообщение.Получатели.Добавить("client@example.com");
Сообщение.Тема = "Счет на оплату №" + СсылкаНаОбъект.Номер;
Сообщение.Тексты.Добавить("Добрый день! Во вложении ваш счет.", ТипТекстаПочтовогоСообщения.ПростойТекст);
// 4. Добавляем двоичные данные как вложение
// Первый параметр - сами данные, второй - имя файла, которое увидит клиент
Сообщение.Вложения.Добавить(ДанныеPDF, "Schet_" + СсылкаНаОбъект.Номер + ".pdf");
// 5. Отправка через почтовый профиль
Почта = Новый ИнтернетПочта;
Почта.Подключиться(Профиль);
Почта.Послать(Сообщение);
Почта.Отключиться();
Этот метод не требует очистки временных каталогов и работает максимально быстро, так как все операции происходят в оперативной памяти сервера.
Иногда требуется более тонкая работа с данными, например, если вы используете промежуточные буферы или специфические методы обработки. В 1С для этих целей предназначен объект ПотокВПамяти. Однако разработчики часто сталкиваются с проблемой, когда сформированный PDF оказывается «пустым» (0 байт).
Выясним причину этой ошибки. Когда мы записываем ТабличныйДокумент в поток с помощью метода Записать(), внутренний указатель потока перемещается в самый конец (на последнюю позицию записанных данных). Если сразу после этого попытаться прочитать данные из потока, система начнет чтение с текущей позиции указателя, где уже ничего нет.
Разберем правильный пример работы с потоками:
Поток = Новый ПотокВПамяти();
// Записываем печатную форму в поток
ТабДок.Записать(Поток, ТипФайлаТабличногоДокумента.PDF);
// КРИТИЧЕСКИ ВАЖНО: Сбрасываем позицию указателя в начало потока
Поток.Перейти(0, ПозицияВПотоке.Начало);
// Теперь можно получить двоичные данные
ДанныеДляПисьма = Поток.ПолучитьДвоичныеДанные();
Сообщение.Вложения.Добавить(ДанныеДляПисьма, "Document.pdf");
Поток.Закрыть();
Важное замечание: Метод ПолучитьДвоичныеДанные() у самого объекта ПотокВПамяти в последних версиях платформы может автоматически считывать всё содержимое, но явная установка позиции через Перейти() гарантирует корректную работу в любых условиях.
Если по каким-то причинам вы используете старые версии платформы (ниже 8.3.10) или архитектура решения требует наличия физического файла, необходимо правильно обрабатывать жизненный цикл временного объекта. Как было замечено в обсуждении, основной проблемой является ошибка «Файл занят другой программой» при попытке удаления файла методом УдалитьФайлы() сразу после отправки.
Разберем ситуацию: объект ИнтернетПочтовоеСообщение хранит ссылку на файл в своей коллекции Вложения. Пока этот объект существует в памяти, операционная система считает файл заблокированным процессом 1С. Чтобы «отпустить» файл, нужно либо дождаться удаления объекта сообщения из памяти, либо принудительно очистить коллекцию.
Рассмотрим правильный алгоритм работы с временным файлом:
ИмяФайла = ПолучитьИмяВременногоФайла("pdf");
ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.PDF);
Сообщение = Новый ИнтернетПочтовоеСообщение;
// Указываем путь к файлу на диске
Сообщение.Вложения.Добавить(ИмяФайла);
// Выполняем отправку
Почта = Новый ИнтернетПочта;
Почта.Подключиться(Профиль);
Почта.Послать(Сообщение);
Почта.Отключиться();
// Чтобы файл удалился, необходимо очистить вложения или уничтожить объект сообщения
Сообщение.Вложения.Очистить();
Попытка
УдалитьФайлы(ИмяФайла);
Исключение
// Если файл всё еще занят, можно оставить его для автоматической очистки системой
КонецПопытки;
Также хорошей практикой является вынос логики формирования и отправки письма в отдельную процедуру. Когда выполнение процедуры завершается, все локальные переменные (включая Сообщение) уничтожаются автоматически, блокировка снимается, и файл можно безопасно удалять в вызывающем коде.
Проанализируем важный нюанс: если ваш сервер 1С работает под управлением операционной системы Linux, метод Записать() для формирования PDF может выдать ошибку или сформировать документ с некорректным отображением шрифтов (например, «квадратики» или иероглифы). Это происходит из-за отсутствия стандартных Windows-шрифтов (Arial, Times New Roman), которые по умолчанию используются в макетах 1С.
Для решения этой проблемы на сервере Linux необходимо установить пакет шрифтов Microsoft. Например, для Ubuntu/Debian команда будет выглядеть так: sudo apt-get install ttf-mscorefonts-installer. После установки шрифтов рекомендуется перезапустить службу сервера 1С.
При выборе между сохранением на диск и использованием оперативной памяти (двоичных данных), руководствуйтесь следующими правилами:
ДвоичныеДанные. Это быстрее и надежнее.Пока ТекущаяДата() < КонДата Цикл для ожидания освобождения файла. Это на 100% загружает ядро процессора («прогрев процессора»). Если вам действительно нужна пауза, используйте метод Приостановить() (в новых версиях) или работу через двоичные данные, где паузы не требуются вовсе.Используя предложенные методики, вы сможете создавать надежные и быстрые механизмы почтовых рассылок, которые не оставляют следов в файловой системе и работают стабильно в любых условиях.