Разработчики 1С иногда сталкиваются с нетипичным поведением платформы: при пошаговом прохождении кода отладчиком (по клавишам F10/F11) файлы успешно загружаются и прикрепляются к документам, но стоит запустить систему в обычном режиме без отладки — и операция перестает выполняться без видимых ошибок.
В этой статье мы подробно проанализируем причины такого поведения, разберем типичные ошибки в коде присоединенных файлов и рассмотрим надежные способы решения проблемы.
Проанализируем ситуационную картину. Когда код отрабатывает исключительно под отладчиком, ключевая причина почти всегда кроется в разнице временного контекста исполнения и работе сборщика мусора платформы 1С. Выделим три основные причины:
Акт.УникальныйИдентификатор()) вместо уникального идентификатора открытой формы (ЭтаФорма.УникальныйИдентификатор), платформа 1С считает временное хранилище "осиротевшим". В обычном режиме сборщик мусора очищает его мгновенно перед вызовом сервера. При пошаговой отладке задержка выполнения удерживает ссылку в стеке, и файл успевает передаться на сервер.ПоместитьФайлы() является синхронным. В современных конфигурациях на базе управляемых форм и тонкого клиента синхронные вызовы файловой системы работают нестабильно и зависят от режима сеанса.Рассмотрим фрагмент кода, в котором проявляется данная проблема при попытке прикрепить файл PDF к документу:
&НаКлиенте
Функция ДобавитьПДФ(Акт, ПДФ)
Файл = Новый Файл(ПДФ);
ВладелецФайла = Акт;
ИмяСоздаваемогоФайла = Файл.ИмяБезРасширения;
ПолноеИмяФайла = ПДФ;
// ОШИБКА: Берется UUID ссылки документа, а не управляемой формы
ИдентификаторФормы = Акт.УникальныйИдентификатор();
ОбщиеНастройки = ФайловыеФункцииСлужебныйКлиентСервер.ОбщиеНастройкиРаботыСФайлами();
ФайловыеФункцииСлужебныйКлиентСервер.ПроверитьВозможностьЗагрузкиФайла(Файл);
ВремяИзмененияУниверсальное = Файл.ПолучитьУниверсальноеВремяИзменения();
ПомещаемыеФайлы = Новый Массив;
Описание = Новый ОписаниеПередаваемогоФайла(Файл.ПолноеИмя, "");
ПомещаемыеФайлы.Добавить(Описание);
ПомещенныеФайлы = Новый Массив;
// Передача некорректного ИдентификаторФормы приводит к очистке хранилища
Если НЕ ПоместитьФайлы(ПомещаемыеФайлы, ПомещенныеФайлы, , Ложь, ИдентификаторФормы) Тогда
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Ошибка при помещении файла во временное хранилище.");
КонецЕсли;
АдресВременногоХранилищаФайла = ПомещенныеФайлы[0].Хранение;
// Вызов сервера для создания записи о файле
ПрисоединенныеФайлыСлужебныйВызовСервера.ДобавитьФайл(
ВладелецФайла,
ИмяСоздаваемогоФайла,
ОбщегоНазначенияКлиентСервер.РасширениеБезТочки(Файл.Расширение),
,
ВремяИзмененияУниверсальное,
АдресВременногоХранилищаФайла);
КонецФункции
Разберем по шагам, как исправить логику привязки временного хранилища. Чтобы временное хранилище существовало до конца выполнения серверной процедуры, его нужно привязать к действующей форме или передать уникальный идентификатор без привязки к конкретной форме.
Если код вызывается из формы, необходимо передавать идентификатор самой формы:
// В контексте формы передаем УникальныйИдентификатор самой формы:
ИдентификаторФормы = ЭтаФорма.УникальныйИдентификатор;
Если код выполняется в общем модуле на клиенте вне контекста формы, создадим новый УникальныйИдентификатор или не будем ограничивать время жизни хранилища формой:
// Создаем независимый идентификатор для временного хранилища
ИдентификаторХранилища = Новый УникальныйИдентификатор;
АдресХранилища = ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла, ИдентификаторХранилища);
Саймый надежный способ загрузки файлов без зависимости от отладчика и интерактивных форм — чтение файла в объект ДвоичныеДанные непосредственно перед отправкой на сервер.
Рассмотрим пример правильного кода чтения и передачи файла:
&НаКлиенте
Процедура ПрикрепитьФайлКДокументу(СсылкаНаДокумент, ПутьКФайлу)
ФайлНаДиске = Новый Файл(ПутьКФайлу);
Если НЕ ФайлНаДиске.Существует() Тогда
Возврат;
КонецЕсли;
// Считываем файл в двоичные данные на клиенте
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ПутьКФайлу);
// Помещаем во временное хранилище без жесткой привязки к UUID документа
АдресХранилища = ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла);
// Вызываем серверный метод обработки
ПередатьФайлНаСервер(СсылкаНаДокумент, ФайлНаДиске.ИмяБезРасширения, ФайлНаДиске.Расширение, АдресХранилища);
КонецПроцедуры
Если код вынесен в расширение конфигурации, проверим дополнительные параметры платформы:
%LOCALAPPDATA%\1C\1cv8 и %APPDATA%\1C\1cv8). При наличии доступа к серверу перезапускаем службу 1C:Предприятие для обновления серверного кэша метаданных.