Как программно отправить присоединенные файлы по почте в 1С:Бухгалтерия 3.0 и избежать блокировки временных файлов

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

При разработке механизмов отправки электронной почты из 1С:Предприятие программисты часто сталкиваются с неприятной ошибкой совместного доступа: Процесс не может получить доступ к файлу, так как этот файл занят другим процессом. Ситуация классическая: мы сохраняем файл во временный каталог, прикрепляем его к письму, а при попытке удалить временную копию получаем отказ системы. Разберем подробно, почему это происходит и как правильно работать с вложениями в современных конфигурациях, таких как Бухгалтерия предприятия 3.0.

Анализируем причину ошибки 0x00000020

Ошибка удаления файла с кодом 32 (0x00000020) означает, что операционная система заблокировала дескриптор файла. В контексте 1С это происходит по нескольким причинам. Во-первых, при добавлении вложения через метод Письмо.Вложения.Добавить("ПутьКФайлу"), объект ИнтернетПочтовоеСообщение может удерживать ссылку на файл до момента полной отправки или до уничтожения самого объекта письма в памяти. Во-вторых, серверное антивирусное ПО часто начинает сканировать файл сразу после его создания в папке Temp, что создает кратковременную, но фатальную блокировку для метода УдалитьФайлы().

Для решения этой проблемы мы можем пойти двумя путями: использовать потоки в памяти, чтобы вообще не создавать файлы на диске, или задействовать возможности Библиотеки стандартных подсистем (БСП), которая уже встроена в Бухгалтерию 3.0.

Решение №1: Использование двоичных данных и методов БСП

В современных конфигурациях 1С файлы хранятся в специальных справочниках (например, ПрисоединенныеФайлы). Прямое обращение к реквизиту ФайлХранилище.Получить() часто возвращает Неопределено, если файлы хранятся в томах на диске, а не в самой базе данных. Поэтому правильный способ получения данных — использование функций общего модуля РаботаСФайлами.

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


// Предположим, мТекСтрока.Ссылка — это ссылка на элемент справочника присоединенных файлов
// Формируем корректное имя файла с расширением
ИмяФайлаДляВложения = мТекСтрока.Наименование + "." + мТекСтрока.Расширение; 

// Получаем двоичные данные файла через БСП
// Функция сама определит, где лежит файл: в базе или в томе на диске
ДвоичныеДанныеФайла = РаботаСФайлами.ДвоичныеДанныеФайла(мТекСтрока.Ссылка, Ложь);  

Если ДвоичныеДанныеФайла <> Неопределено Тогда
    // Добавляем вложение напрямую из двоичных данных
    Письмо.Вложения.Добавить(ДвоичныеДанныеФайла, ИмяФайлаДляВложения);
КонецЕсли;

Этот метод наиболее предпочтителен, так как он полностью исключает работу с файловой системой сервера вручную и решает проблему блокировок.

Решение №2: Использование потоков (Memory Stream)

Если вам все же необходимо манипулировать данными перед отправкой, проанализируем подход с использованием объекта ПотокВПамяти. Этот механизм появился в платформе версии 8.3.9 и позволяет работать с данными в оперативной памяти, минуя диск.

Посмотрим на пример реализации:


ПотокВПамяти = Новый ПотокВПамяти();
                
// Получаем объект из хранилища (если он там есть)
МойФайлХранилище = мТекСтрока.ФайлХранилище.Получить();

Если ТипЗнч(МойФайлХранилище) = Тип("ДвоичныеДанные") Тогда
    МойФайлХранилище.Записать(ПотокВПамяти);
    // Извлекаем данные из потока
    ДД = ПотокВПамяти.ЗакрытьИПолучитьДвоичныеДанные();
    Письмо.Вложения.Добавить(ДД, мТекСтрока.Наименование);
КонецЕсли;

Важное замечание: использование ПотокВПамяти оправдано только для небольших файлов. Если вы обрабатываете очень тяжелые документы (сотни мегабайт), хранение их в оперативной памяти может привести к падению рабочего процесса rphost. В таких случаях лучше использовать временные файлы, но с корректной обработкой исключений при удалении.

Как правильно добавлять файлы вручную, чтобы не терять расширение

Часто программисты сталкиваются с тем, что при программном добавлении файла в справочник присоединенных файлов, он "теряет" расширение или не открывается. Это происходит из-за некорректного заполнения параметров в методах БСП. Проанализируем правильный алгоритм добавления файла:

  1. Подготавливаем двоичные данные файла.
  2. Помещаем их во временное хранилище.
  3. Заполняем структуру параметров для БСП, разделяя имя и расширение.

Пример корректного кода для регистрации нового файла в системе:


// Читаем файл с диска или получаем из другого источника
ДвоичныеДанные = Новый ДвоичныеДанные(ПутьКФайлуНаДиске);
АдресВХранилище = ПоместитьВоВременноеХранилище(ДвоичныеДанные);

// Расширение должно быть без точки (например, "pdf")
ПараметрыФайла = Новый Структура;
ПараметрыФайла.Вставить("Автор", Пользователи.ТекущийПользователь());
ПараметрыФайла.Вставить("ВладелецФайлов", СсылкаНаОбъектВладелец); // Например, Ссылка на ЗаказПоставщику
ПараметрыФайла.Вставить("ИмяБезРасширения", ИмяФайлаБезРасширения);
ПараметрыФайла.Вставить("РасширениеБезТочки", "pdf"); 
ПараметрыФайла.Вставить("ВремяИзмененияУниверсальное", ТекущаяДатаСеанса());

// Используем стандартный метод БСП для создания записи в справочнике
ПрисоединенныйФайл = РаботаСФайлами.ДобавитьФайл(ПараметрыФайла, АдресВХранилище);

Рекомендации по очистке временных файлов

Если специфика вашей задачи требует обязательного создания физического файла на диске (например, для внешней обработки сторонней утилитой), следуйте этим правилам, чтобы минимизировать ошибки:

Использование подсистемы «Взаимодействия»

В конфигурации Бухгалтерия 3.0 есть еще более высокоуровневый способ отправки почты — подсистема «Взаимодействия». Вместо того чтобы вручную создавать объект ИнтернетПочта, мы можем программно создать документ ЭлектронноеПисьмоИсходящее.

Преимущество этого метода в том, что система сама берет на себя все заботы по извлечению двоичных данных из хранилища, формированию структуры MIME-сообщения и корректной обработке расширений. Вам достаточно просто передать массив ссылок на присоединенные файлы в табличную часть или параметры создания письма. Это исключает любые ошибки 0x00000020, так как платформа сама управляет жизненным циклом временных объектов.

Таким образом, для решения проблемы "неудаляемых файлов" лучшим выбором будет переход на использование двоичных данных через функции РаботаСФайлами.ДвоичныеДанныеФайла(), что обеспечит стабильную работу сервера и чистоту временных каталогов.

← На главную