В процессе разработки часто возникает задача автоматической отправки печатных форм документов клиентам. Самым популярным форматом для этого является PDF. Однако при попытке программной реализации этой задачи разработчики сталкиваются с техническими трудностями: либо с правами доступа к файловой системе, либо с блокировками файлов, из-за которых их невозможно корректно удалить после отправки.
Давайте разберем, как правильно реализовать процесс формирования и отправки PDF-вложений, чтобы избежать ошибок блокировки ресурсов.
Самый распространенный способ — сохранение табличного документа во временный файл. Основная проблема здесь заключается в том, что система может «удерживать» файл заблокированным до тех пор, пока объект почтового сообщения не будет выгружен из памяти.
Для корректной работы выполним следующие действия:
Записать.Рассмотрим пример кода:
// Получаем путь к временному файлу
ИмяФайла = ПолучитьИмяВременногоФайла("pdf");
// Сохраняем табличный документ
ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.PDF);
// Формируем почтовое сообщение
ПочтовоеСообщение = Новый ИнтернетПочтовоеСообщение;
ПочтовоеСообщение.Вложения.Добавить(ИмяФайла, "Счет.pdf");
// Отправляем
Почта = Новый ИнтернетПочта;
Почта.Подключиться(Профиль);
Почта.Послать(ПочтовоеСообщение);
Почта.Отключиться();
// Важный этап: очистка ресурсов
ПочтовоеСообщение = Неопределено;
УдалитьФайлы(ИмяФайла);
Если файл все равно не удаляется, скорее всего, объект ИнтернетПочтовоеСообщение продолжает ссылаться на дескриптор файла. Присвоение переменной значения Неопределено помогает сборщику мусора освободить ресурсы.
Более прогрессивный метод, позволяющий избежать записи на диск, — работа с объектом ДвоичныеДанные. Этот подход исключает конфликт блокировок на уровне операционной системы.
При использовании ПотокВПамяти (MemoryStream) важно помнить одну особенность: после записи данных в поток указатель (позиция) находится в конце. Перед тем как считать содержимое, необходимо сбросить позицию в начало.
Алгоритм действий:
ПотокВПамяти.ТабличныйДокумент.ДвоичныеДанные и добавляем их во вложение напрямую, минуя файловую систему.Пример реализации без использования диска:
Поток = Новый ПотокВПамяти;
ТабДок.Записать(Поток, ТипФайлаТабличногоДокумента.PDF);
// Сбрасываем позицию потока в начало
Поток.УстановитьПозицию(0);
// Получаем двоичные данные
ДвоичныеДанныеPDF = Поток.ПолучитьДвоичныеДанные();
// Добавляем вложение в почту
ПочтовоеСообщение.Вложения.Добавить(ДвоичныеДанныеPDF, "Счет.pdf");
Если ваша задача подразумевает отправку большого количества писем в цикле, рекомендуем придерживаться следующих правил:
ИнтернетПочта сразу после завершения операции, чтобы не держать сетевые и файловые ресурсы открытыми лишнее время.Использование ДвоичныхДанных в оперативной памяти является предпочтительным способом, так как он делает ваше решение независимым от настроек прав доступа сервера и ограничений файловой системы, что делает код более отказоустойчивым и переносимым между различными средами (файловый вариант или клиент-серверный).