При передаче файлов с клиента на сервер через временное хранилище разработчики регулярно сталкиваются с потерей исходных атрибутов файла. При помещении файла во временное хранилище и последующей его выгрузке на стороне сервера создается новый локальный файл, дата модификации которого соответствует моменту выгрузки на сервер, а не моменту реального изменения исходного файла пользователем.
Попытка прочитать дату модификации на клиенте классическим синхронным методом ПолучитьВремяИзменения() в современных конфигурациях вызывает исключение: «Режим использования синхронных методов отключён». Разберем по шагам, почему возникает эта ошибка, как правильно прочитать время изменения на клиенте и как восстановить или сохранить его на стороне сервера.
Проанализируем ситуацию: в конфигурациях на платформе 1С:Предприятие 8.3 для обеспечения кроссплатформенности и работы в веб-клиенте свойство конфигурации «Режим использования синхронных вызовов расширений платформы и внешних компонент» переводится в значение «Не использовать». В этом режиме любые прямые синхронные вызовы к файловой системе на клиенте (например, Файл.ПолучитьВремяИзменения() или синхронные диалоги выбора файлов) полностью блокируются платформой.
Следовательно, прямое обращение к свойствам файла должно быть переписано с использованием асинхронной модели платформы.
Рассмотрим традиционный асинхронный подход, доступный в управляемом приложении. Для получения времени последнего изменения исходного файла на клиенте используется метод НачатьПолучениеВремениИзменения() или метод для работы в шкале UTC — НачатьПолучениеУниверсальногоВремениИзменения().
Разберем реализацию этого сценария:
Файл с путем к исходному файлу.ОписаниеОповещения, указывающий на процедуру обратного вызова.Посмотрим на пример кода на клиенте:
&НаКлиенте
Процедура ПроверитьИОтправитьФайл(ПутьКФайлу)
ФайлНаКлиенте = Новый Файл(ПутьКФайлу);
Оповещение = Новый ОписаниеОповещения("ПослеПолученияВремени", ЭтотОбъект, ПутьКФайлу);
// Получаем универсальное время изменения (UTC), чтобы избежать расхождений часовых поясов
ФайлНаКлиенте.НачатьПолучениеУниверсальногоВремениИзменения(Оповещение);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПолученияВремени(ВремяМодификации, ДополнительныеПараметры) Экспорт
Если ВремяМодификации = Неопределено Тогда
Возврат;
КонецЕсли;
ПутьКФайлу = ДополнительныеПараметры;
// Помещаем файл во временное хранилище
АдресХранилища = "";
ПоместитьФайл(АдресХранилища, ПутьКФайлу, , Ложь, УникальныйИдентификатор);
// Передаем адрес во временном хранилище и дату модификации на сервер
ОбработатьФайлНаСервере(АдресХранилища, ВремяМодификации);
КонецПроцедуры
Если конфигурация разрабатывается для современных версий платформы 1С:Предприятие 8.3 с поддержкой асинхронных функций, код можно существенно упростить без создания лишних процедур оповещения. Используем оператор Ждать и метод НайтиФайлыАсинх().
Посмотрим на пример реализации в асинхронной процедуре:
&НаКлиенте
Асинх Процедура ЗагрузитьФайлСКлиента(ПолныйПутьКФайлу)
// Асинхронно находим файл, чтобы безопасно прочитать его свойства
НайденныеФайлы = Ждать НайтиФайлыАсинх(ПолныйПутьКФайлу);
Если НайденныеФайлы.Количество() = 0 Тогда
Возврат;
КонецЕсли;
ФайлКлиента = НайденныеФайлы[0];
ВремяИзмененияUTC = Ждать ФайлКлиента.ПолучитьУниверсальноеВремяИзмененияАсинх();
// Помещаем файл во временное хранилище асинхронным методом
РезультатПомещения = Ждать ПоместитьФайлВХранилищеАсинх(ПолныйПутьКФайлу, , УникальныйИдентификатор);
Если РезультатПомещения <> Неопределено И РезультатПомещения.Помещен Тогда
ОбработатьФайлНаСервере(РезультатПомещения.Адрес, ВремяИзмененияUTC);
КонецЕсли;
КонецПроцедуры
Когда адрес временного хранилища и точное время изменения получены на стороне сервера, у разработчика есть два варианта решения задачи в зависимости от бизнес-логики.
Вариант А: Физическая установка даты файла на диске сервера
Если серверный алгоритм требует наличия физического файла на диске сервера со старыми атрибутами, нет необходимости вызывать системные утилиты ОС или скрипты. Объект Файл в среде 1С на сервере поддерживает штатные синхронные методы модификации системного времени: УстановитьВремяИзменения() и УстановитьУниверсальноеВремяИзменения().
Разберем код серверной процедуры:
&НаСервере
Процедура ОбработатьФайлНаСервере(АдресХранилища, ВремяИзмененияUTC)
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресХранилища);
ПутьНаСервере = ПолучитьИмяВременногоФайла("tmp");
ДвоичныеДанные.Записать(ПутьНаСервере);
// Создаем объект Файл на сервере и синхронно выставляем сохраненное время
ФайлНаСервере = Новый Файл(ПутьНаСервере);
ФайлНаСервере.УстановитьУниверсальноеВремяИзменения(ВремяИзмененияUTC);
// Далее файл обрабатывается сервером с корректным временем в файловой системе
КонецПроцедуры
Вариант Б: Сохранение метаданных в базе данных
В типовых конфигурациях считается хорошей практикой не зависеть от файловых атрибутов операционной системы сервера. Если файл регистрируется в системе (например, в справочнике присоединенных файлов или регистре сведений), переданное значение ВремяИзмененияUTC просто сохраняется в реквизит с типом Дата. Это исключает любые искажения, связанные с кэшированием или особенностями файловых систем серверов под управлением Linux и Windows.
НачатьПолучениеУниверсальногоВремениИзменения() / УстановитьУниверсальноеВремяИзменения(). Использование локального времени приведет к сдвигам дат, если сервер и рабочее место клиента находятся в разных регионах.ПодключитьРасширениеРаботыСФайлами().