Переход с файловой версии базы данных на клиент-серверную часто сопровождается трудностями при работе с файловой системой. Обычная команда КопироватьФайл, которая отлично работала на локальном компьютере, внезапно начинает выдавать ошибки или просто ничего не делает при выполнении на сервере. В этой статье мы подробно разберем, почему это происходит, и рассмотрим правильные архитектурные подходы к решению этой задачи.
Прежде чем переходить к написанию кода, давайте проанализируем ситуацию и выясним основные причины возникновения ошибок в клиент-серверном режиме:
&НаСервере, код исполняется на физическом сервере, где установлен «Агент сервера 1С:Предприятие». Переменные, содержащие пути вроде C:\MyFiles\photo.jpg, сервер будет искать на своих собственных локальных дисках, а не на компьютере пользователя.USR1CV8) по умолчанию не имеет прав доступа к сетевым ресурсам (UNC-путям вида \\ServerName\Share). Даже если у текущего пользователя Windows есть доступ к папке, у сервера его может не быть.ДиалогВыбораФайла, так как там нет графического интерфейса пользователя.Самый простой и логичный способ, если пользователю доступны сетевые ресурсы — выполнить копирование прямо на клиенте. В этом случае 1С использует права текущего пользователя Windows, и нам не нужно настраивать доступ для системной службы сервера.
Разберем пример кода, который позволяет выбрать файл и скопировать его в нужную структуру папок на сетевом диске:
&НаКлиенте
Процедура ДобавитьФайлЭскиза(Команда)
// 1. Выбираем файл на компьютере пользователя
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Заголовок = "Выберите файл эскиза";
Диалог.МножественныйВыбор = Ложь;
Если Диалог.Выбрать() Тогда
ПолныйПутьИсточник = Диалог.ПолноеИмяФайла;
ИмяФайла = Диалог.ИмяФайла;
// 2. Получаем необходимые данные с сервера (номер документа, имя покупателя)
ДанныеДляПути = ПолучитьДанныеДляСозданияКаталога(Объект.Ссылка);
// Формируем сетевой путь
БазовыйКаталог = "\\192.168.0.249\эскизы\";
ИмяПапки = ДанныеДляПути.ПокупательНаименование + " Заявка №" + ДанныеДляПути.Номер + " от " + Формат(ДанныеДляПути.Дата, "ДФ=dd.MM.yyyy");
ЦелевойКаталог = БазовыйКаталог + ИмяПапки;
ПолныйПутьНазначение = ЦелевойКаталог + "\" + ИмяФайла;
// 3. Создаем каталог и копируем файл прямо на клиенте
Попытка
СоздатьКаталог(ЦелевойКаталог);
КопироватьФайл(ПолныйПутьИсточник, ПолныйПутьНазначение);
// 4. Записываем информацию в базу данных
ДобавитьЗаписьВТЧНаСервере(ПолныйПутьНазначение, ИмяФайла);
Сообщить("Файл успешно скопирован.");
Исключение
Сообщить("Ошибка при копировании файла: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
КонецЕсли;
КонецПроцедуры
Обратите внимание, что функции СоздатьКаталог и КопироватьФайл доступны на клиенте в тонком клиенте Windows, что делает этот метод наиболее быстрым в реализации.
Рассмотрим более правильный с точки зрения архитектуры 1С подход. Представим ситуацию, когда сервер должен сам обработать файл или если доступ к сетевому хранилищу есть только у сервера (например, по соображениям безопасности). В этом случае мы должны передать файл с клиента на сервер через ВременноеХранилище.
Рассмотрим этот процесс по шагам:
&НаКлиенте
Асинхронная Процедура ЗагрузитьФайлНаСервер()
// Используем асинхронный метод для выбора файла (современный стандарт)
Описание = Новый ОписаниеПередаваемогоФайла;
Результат = Ждать ПоместитьФайлИнтерактивноАсинхронно(Описание, "Выберите файл", РежимДиалогаВыбораФайла.Открытие);
Если Результат <> Неопределено Тогда
// Адрес во временном хранилище получен
АдресХранилища = Результат.Адрес;
ИмяФайла = Результат.СсылкаНаФайл.Файл.Имя;
// Вызываем серверную процедуру для сохранения
СохранитьФайлИзХранилищаНаСервере(АдресХранилища, ИмяФайла);
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура СохранитьФайлИзХранилищаНаСервере(АдресХранилища, ИмяФайла)
// Формируем путь (сервер должен иметь доступ к этому UNC-пути!)
ПутьКПапке = "\\1CSERVER\Files\Drawings\";
СоздатьКаталог(ПутьКПапке);
// Получаем данные из хранилища
ДанныеФайла = ПолучитьИзВременногоХранилища(АдресХранилища);
Если ТипЗнч(ДанныеФайла) = Тип("ДвоичныеДанные") Тогда
ДанныеФайла.Записать(ПутьКПапке + ИмяФайла);
// Добавляем строку в табличную часть
НоваяСтрока = Объект.ТЧЭскизы.Добавить();
НоваяСтрока.ПутьКФайлу = ПутьКПапке + ИмяФайла;
// ... заполнение остальных реквизитов
КонецЕсли;
КонецПроцедуры
Проанализируем потенциальную проблему: в коде часто используется Объект.Покупатель.Наименование для создания папки. Это опасный момент. Если в наименовании контрагента встретятся символы /, \, *, :, ?, ", <, > или |, функция СоздатьКаталог вызовет исключение, так как эти символы запрещены в именах папок Windows.
Рекомендуем использовать вспомогательную функцию для «очистки» строки от недопустимых знаков:
Функция ОчиститьИмяФайла(Знач Имя) Экспорт
ЗапрещенныеСимволы = "/\:*?""<>|";
Для Индекс = 1 По СтрДлина(ЗапрещенныеСимволы) Цикл
Символ = Сред(ЗапрещенныеСимволы, Индекс, 1);
Имя = СтрЗаменить(Имя, Символ, "");
КонецЦикла;
Возврат СократЛП(Имя);
КонецФункции
Если вы решили сохранять файлы именно НаСервере, необходимо убедиться, что служба сервера имеет права на запись в сетевую папку. Рассмотрим, как это проверить:
LocalSystem или USR1CV8, то доступа к сети, скорее всего, нет.DOMAIN\SERVERNAME$).Выясним еще один момент: если ваш сервер 1С работает под управлением Linux, то пути вида \\192.168.0.249\share работать не будут. В Linux сетевые папки нужно сначала смонтировать в файловую систему (например, в /mnt/drawings), и в коде 1С обращаться уже к этому локальному пути.
Мы рассмотрели два основных подхода к копированию файлов. Выбор зависит от ваших требований к безопасности и прав доступа. Для простых задач идеально подходит выполнение НаКлиенте, так как это минимизирует нагрузку на сервер и упрощает работу с правами доступа. Для более сложных систем, где требуется централизованная обработка, используйте передачу через Временное хранилище. Всегда помните об обработке запрещенных символов в путях и проверяйте контекст выполнения кода (Клиент/Сервер), чтобы избежать ошибок «Файл не найден».