При разработке функционала для загрузки данных из файлов в 1С в клиент-серверном режиме часто возникает ошибка доступа. Пользователь выбирает файл на своем компьютере, но система сообщает "Каталог не обнаружен", указывая на локальный путь к этому файлу. Это ставит в тупик, ведь кажется, что операция выполняется на клиенте, где файл точно существует.
Рассмотрим, почему возникает эта проблема и как правильно организовать загрузку файлов с клиентского компьютера на сервер для последующей обработки (например, когда проводится анализ изменений конфигурации).
Давайте проанализируем исходный код, который приводит к ошибке. Разработчик пытается прочитать Excel-файл прямо на клиенте после его выбора в диалоге.
&НаКлиенте
Процедура ЗагрузитьФайлСДиска(Команда)
// ... пропущена проверка ...
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Заголовок = "Выбор файла";
Диалог.Фильтр = "Excel файлы(*.xls;*.xlsx)|*.xls;*.xlsx";
Если Диалог.Выбрать Тогда
ПутьКЗагрузке = Диалог.ПолноеИмяФайла;
ПрочитатьФайл(ПутьКЗагрузке); // Вызов процедуры чтения
// ... пропущен код для прикрепления файла ...
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Функция ПрочитатьФайл(ПутьКЗагрузке)
ДанныеExcel = Новый ТабличныйДокумент;
// !!! Вот здесь возникает ошибка !!!
ДанныеExcel.Прочитать(ПутьКЗагрузке, СпособЧтенияЗначенийТабличногоДокумента.Значение);
ДанныеЗагрузки = Объект.ДанныеЗагрузки.Выгрузить;
ПрочитатьФайлExcelНаСервере(ДанныеExcel, ДанныеЗагрузки);
Объект.ДанныеЗагрузки.Загрузить(ДанныеЗагрузки);
Модифицированность = Истина;
КонецФункции
Текст ошибки выглядит так:
Ошибка при вызове метода контекста (Прочитать) ... по причине: Каталог не обнаружен 'C:\Users\Admin\Downloads\Пример выгузки.xlsx' (подобная ошибка часто встречается, когда настраивается экспорт справочников в Excel).
Главная причина ошибки — это фундаментальное разделение контекстов выполнения в 1С, что требует особого подхода при копировании файлов между клиентом и сервером:
Хотя в коде вызов метода ДанныеExcel.Прочитать(ПутьКЗагрузке) находится в клиентской процедуре, сам метод Прочитать объекта ТабличныйДокумент в управляемых формах для тонкого и веб-клиента выполняется на сервере из соображений безопасности и архитектуры. Платформа пытается найти файл по пути 'C:\Users\Admin\Downloads\...' на жестком диске сервера, а не клиента. Естественно, такого каталога там нет, что и приводит к ошибке.
Чтобы сервер смог обработать файл, его нужно сначала передать с клиента на сервер. Для этого в платформе 1С существует специальный механизм — временное хранилище. Это безопасная область на сервере, куда можно поместить данные с клиента и получить уникальный адрес (ссылку) на них. Затем этот адрес передается в серверный вызов, и сервер по этому адресу получает данные для обработки.
Наиболее современным и рекомендуемым способом является асинхронная передача файла. Это означает, что интерфейс пользователя не будет блокироваться (не "зависнет") на время копирования файла, что особенно важно при работе с большими файлами или медленным интернет-соединением.
Разберем этот процесс по шагам.
Шаг 1. На клиенте: инициируем передачу файла
Вместо стандартного ДиалогВыбораФайла и последующего чтения используем специальный метод НачатьПомещениеФайла. Этот метод сам покажет пользователю диалог выбора файла и начнет его передачу на сервер в фоновом режиме.
Важнейший параметр этого метода — ОписаниеОповещения. В нем мы указываем, какую процедуру нужно вызвать после того, как файл будет успешно (или неуспешно) помещен в хранилище.
Шаг 2. На клиенте: обрабатываем результат передачи
Создаем процедуру-обработчик, имя которой мы указали в ОписанииОповещения. Эта процедура получит результат операции. Если все прошло успешно, в ее параметрах будет структура с адресом файла во временном хранилище. Получив этот адрес, мы вызываем серверную процедуру и передаем его туда.
Шаг 3. На сервере: получаем файл из хранилища и обрабатываем его
Серверная процедура принимает адрес во временном хранилище. С помощью функции ПолучитьИзВременногоХранилища() она извлекает двоичные данные файла. После этого с данными можно работать: например, создать из них табличный документ и прочитать его содержимое.
Теперь давайте перепишем исходный код, используя правильный асинхронный подход.
// &НаКлиенте
// Процедура-инициатор. Вызывается по нажатию кнопки на форме.
Процедура ЗагрузитьИзФайла(Команда)
// Создаем описание оповещения, которое сработает после выбора и передачи файла.
// Указываем имя процедуры-обработчика: "ЗагрузитьИзФайлаЗавершение".
Оповещение = Новый ОписаниеОповещения("ЗагрузитьИзФайлаЗавершение", ЭтотОбъект);
// Используем асинхронный метод для помещения файла на сервер
НачатьПомещениеФайла(Оповещение, , , Истина, УникальныйИдентификатор);
КонецПроцедуры
// &НаКлиенте
// Процедура-обработчик. Вызывается платформой автоматически после завершения операции.
// Экспортная, так как вызывается из оповещения.
Процедура ЗагрузитьИзФайлаЗавершение(Результат, Адрес, ИмяФайла, ДополнительныеПараметры) Экспорт
// Если пользователь отменил выбор файла или произошла ошибка передачи
Если Не Результат Тогда
Возврат;
КонецЕсли;
// Вызываем серверную процедуру и передаем ей адрес файла во временном хранилище
// и его исходное имя.
ПрочитатьФайлНаСервере(Адрес, ИмяФайла);
КонецПроцедуры
// &НаСервере
// Серверная процедура для основной работы с файлом.
Процедура ПрочитатьФайлНаСервере(Адрес, ИмяФайла)
// Получаем двоичные данные файла из временного хранилища по его адресу
ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(Адрес);
// Если данные не получены, прекращаем выполнение
Если ДвоичныеДанныеФайла = Неопределено Тогда
Возврат;
КонецЕсли;
// Создаем табличный документ для работы с Excel
ТабличныйДокумент = Новый ТабличныйДокумент;
// Для того чтобы метод Прочитать сработал с двоичными данными,
// их нужно сначала записать во временный файл на сервере.
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(РасширениеИзИмениФайла(ИмяФайла));
ДвоичныеДанныеФайла.Записать(ИмяВременногоФайла);
// Теперь читаем данные из временного файла, который находится на сервере
Попытка
ТабличныйДокумент.Прочитать(ИмяВременногоФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Исключение
// В случае ошибки чтения файла (например, неверный формат) сообщаем пользователю
ОписаниеОшибки = ОписаниеОшибки();
ОбщегоНазначения.СообщитьПользователю("Не удалось прочитать файл Excel. " + ОписаниеОшибки);
Возврат;
КонецПопытки;
// После успешного чтения не забываем удалить временный файл
УдалитьФайлы(ИмяВременногоФайла);
// Теперь в переменной "ТабличныйДокумент" находятся данные из Excel.
// Можно приступать к их разбору и загрузке в табличную часть документа.
// Например:
Объект.ДанныеЗагрузки.Очистить();
Для Сч = 2 По ТабличныйДокумент.ВысотаТаблицы Цикл // Начинаем со 2-й строки, пропуская заголовок
НоваяСтрока = Объект.ДанныеЗагрузки.Добавить();
НоваяСтрока.Артикул = ТабличныйДокумент.Область(Сч, 1).Текст;
НоваяСтрока.Наименование = ТабличныйДокумент.Область(Сч, 2).Текст;
НоваяСтрока.Количество = Число(ТабличныйДокумент.Область(Сч, 3).Текст);
КонецЦикла;
Модифицированность = Истина;
КонецПроцедуры
// &НаСервереБезКонтекста
// Вспомогательная функция для получения расширения из имени файла.
Функция РасширениеИзИмениФайла(ИмяФайла)
Возврат Сред(ИмяФайла, Найти(ИмяФайла, ".") + 1);
КонецФункции
Чтобы избежать ошибки "Каталог не обнаружен" и корректно организовать работу с файлами в клиент-серверном режиме, всегда следуйте этому правилу:
Сначала передайте файл с клиента на сервер через временное хранилище, и только потом выполняйте его обработку на сервере.
Использование асинхронных методов, таких как НачатьПомещениеФайла, является современной и рекомендуемой практикой, которая обеспечивает отзывчивость интерфейса и корректную работу в любых режимах клиента, включая веб-клиент.