Загрузка файла пользователя на сервер 1С через временное хранилище
Содержание
Чтобы передать файл с клиента на сервер в 1С 8.3, его помещают во временное хранилище методом НачатьПомещениеФайлаНаСервер (платформа 8.3.13 и выше) или НачатьПомещениеФайла. Клиент получает адрес, передаёт его в серверную процедуру, а сервер достаёт ДвоичныеДанные через ПолучитьИзВременногоХранилища и пишет их на диск или в реквизит.
Прямого «копирования» с компьютера пользователя на сервер в 1С нет. Клиент и сервер — разные процессы, часто на разных машинах, и общаются только через вызовы сервера. Поэтому любая загрузка идёт в два хода: клиент отдаёт данные платформе, сервер забирает их по адресу.
Почему файл нельзя просто открыть по пути на сервере
Частая ошибка новичка — передать на сервер строку C:\Users\Ivanov\Desktop\price.xlsx и попытаться прочитать её там. В клиент-серверном варианте сервер ищет этот путь у себя, на диске сервера 1С. Файла там нет, и вы получаете ошибку доступа или «файл не найден».
Что нужно держать в голове:
- 🖥️ Код
&НаКлиентевидит файловую систему компьютера пользователя (в веб-клиенте — только через диалог или расширение работы с файлами). - 🗄️ Код
&НаСервереработает в рабочем процессеrphostи видит только диски сервера и сетевые ресурсы, доступные учётной записи службы. - 📦 Мост между ними — временное хранилище. Клиент кладёт туда двоичные данные, сервер забирает по адресу-строке.
- 💾 Файловый вариант базы ведёт себя иначе: «сервер» выполняется на той же машине, поэтому ошибка с путями маскируется. Код, написанный и проверенный на файловой базе, ломается при переходе на сервер. Тестируйте сразу в клиент-серверном режиме.
Какой способ выбрать
| Способ | Платформа | Когда подходит | Ограничения |
|---|---|---|---|
НачатьПомещениеФайлаНаСервер | 8.3.13+ | Новые разработки, нужен прогресс и отмена | Не работает на старых платформах |
ПоместитьФайлНаСерверАсинх + Ждать | 8.3.18+ | Тот же метод, но код читается линейно | Процедура должна быть Асинх |
НачатьПомещениеФайла | 8.3 | Совместимость со старыми релизами | Нет прогресса, без расширения в веб-клиенте только диалог |
НачатьПомещениеФайлов | 8.3 | Несколько файлов за раз | То же, что выше |
ПоместитьФайл | 8.3 | Только старые конфигурации с модальностью | Синхронный, при запрете модальности вызывает ошибку |
Новый ДвоичныеДанные(Путь) на клиенте + параметр серверной процедуры | 8.3 | Тонкий клиент, путь известен, файл небольшой | Не работает в веб-клиенте, весь файл идёт одним вызовом |
Если конфигурация собрана в режиме совместимости ниже 8.3.13, новые методы будут недоступны даже на свежей платформе. Режим смотрят в свойствах корня конфигурации, свойство «Режим совместимости».
Шаг 1. Выбор и чтение файла на клиенте
Начнём с современного варианта. Форма с командой «Загрузить файл», пользователь выбирает файл в диалоге, платформа сама передаёт его на сервер.
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
ПараметрыДиалога = Новый ПараметрыДиалогаПомещенияФайлов;
ПараметрыДиалога.Заголовок = "Выберите файл для загрузки";
ПараметрыДиалога.Фильтр = "Таблицы Excel (*.xlsx)|*.xlsx|Все файлы (*.*)|*.*";
Оповещение = Новый ОписаниеОповещения("ЗагрузитьФайлЗавершение", ЭтотОбъект);
НачатьПомещениеФайлаНаСервер(Оповещение, , , , ПараметрыДиалога, УникальныйИдентификатор);
КонецПроцедуры
Пустые параметры между оповещением и параметрами диалога — это оповещение о ходе выполнения, оповещение перед началом и адрес хранилища. Адрес можно не указывать: платформа создаст новый.
Последний параметр — УникальныйИдентификатор формы. Передавайте его всегда. Тогда данные живут во временном хранилище, пока открыта форма. Без идентификатора платформа может удалить значение раньше, чем до него дойдёт серверный код.
Обработчик завершения
&НаКлиенте
Процедура ЗагрузитьФайлЗавершение(ОписаниеФайла, ДополнительныеПараметры) Экспорт
Если ОписаниеФайла = Неопределено
Или ОписаниеФайла.ПомещениеФайлаОтменено Тогда
Возврат;
КонецЕсли;
ИмяФайла = ОписаниеФайла.СсылкаНаФайл.Имя;
ЗаписатьФайлНаСервере(ОписаниеФайла.Адрес, ИмяФайла);
КонецПроцедуры
Процедура-обработчик обязательно Экспорт, иначе ОписаниеОповещения её не вызовет. Объект ОписаниеПомещенногоФайла содержит адрес во временном хранилище, признак отмены и ссылку на файл, из которой берут имя.
Тот же код через Асинх и Ждать
На платформе 8.3.18 и выше обработчик можно убрать:
&НаКлиенте
Асинх Процедура ЗагрузитьФайлАсинх(Команда)
ПараметрыДиалога = Новый ПараметрыДиалогаПомещенияФайлов;
ПараметрыДиалога.Заголовок = "Выберите файл для загрузки";
ОписаниеФайла = Ждать ПоместитьФайлНаСерверАсинх(, , , ПараметрыДиалога, УникальныйИдентификатор);
Если ОписаниеФайла = Неопределено
Или ОписаниеФайла.ПомещениеФайлаОтменено Тогда
Возврат;
КонецЕсли;
ЗаписатьФайлНаСервере(ОписаниеФайла.Адрес, ОписаниеФайла.СсылкаНаФайл.Имя);
КонецПроцедуры
Файл с известным путём, без диалога
Если путь уже известен (например, обработка забирает выгрузку из C:\Exchange\out.xml), диалог не нужен. Используйте НачатьПомещениеФайла с параметром Интерактивно = Ложь:
&НаКлиенте
Процедура ЗагрузитьВыгрузку(Команда)
Оповещение = Новый ОписаниеОповещения("ЗагрузитьВыгрузкуЗавершение", ЭтотОбъект);
НачатьПомещениеФайла(Оповещение, , "C:\Exchange\out.xml", Ложь, УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ЗагрузитьВыгрузкуЗавершение(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
Если Не Результат Тогда
Сообщить("Файл не передан: " + ВыбранноеИмяФайла);
Возврат;
КонецЕсли;
ЗаписатьФайлНаСервере(Адрес, "out.xml");
КонецПроцедуры
В веб-клиенте неинтерактивное помещение требует установленного расширения работы с файлами. Об этом — в разделе про веб-клиент.
Шаг 2. Передача адреса в серверный контекст
Через вызов сервера передают адрес (строку), а не сами данные. Это главное правило для форм.
Почему не стоит передавать ДвоичныеДанные параметром:
- 📉 При вызове
&НаСервереиз формы на сервер уходят и данные формы. Тяжёлый параметр плюс большая форма — заметная задержка. - 🔁 Данные в хранилище уже лежат на сервере. Передавая их параметром, вы гоните файл по сети второй раз.
- 🧩 Адрес можно отдать в фоновое задание или общий модуль — там нет формы, но хранилище доступно.
Если серверная процедура не трогает реквизиты формы, объявляйте её &НаСервереБезКонтекста. Тогда данные формы на сервер не передаются вообще:
&НаСервереБезКонтекста
Процедура ЗаписатьФайлНаСервере(Адрес, ИмяФайла)
// код записи — в следующем разделе
КонецПроцедуры
Для обработки длинных файлов (загрузка прайса на 50 000 строк) запускайте разбор в фоновом задании и передавайте ему адрес. Пользователь не ждёт с замороженной формой, а сервер не упирается в таймаут веб-сервера.
Шаг 3. Запись файла на сервере: каталог, реквизит или присоединённый файл
На сервере данные достают одной строкой и дальше решают, куда их положить.
Запись в каталог на диске сервера
&НаСервереБезКонтекста
Процедура ЗаписатьФайлНаСервере(Адрес, ИмяФайла)
ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(Адрес);
Если ТипЗнч(ДвоичныеДанныеФайла) <> Тип("ДвоичныеДанные") Тогда
ВызватьИсключение "Данные файла не найдены во временном хранилище";
КонецЕсли;
БезопасноеИмя = СтрЗаменить(ИмяФайла, "\", "_");
БезопасноеИмя = СтрЗаменить(БезопасноеИмя, "/", "_");
БезопасноеИмя = СтрЗаменить(БезопасноеИмя, "..", "_");
Каталог = "D:\1C_Files\Incoming\";
ПолноеИмя = Каталог + Строка(Новый УникальныйИдентификатор) + "_" + БезопасноеИмя;
ДвоичныеДанныеФайла.Записать(ПолноеИмя);
УдалитьИзВременногоХранилища(Адрес);
КонецПроцедуры
Три детали, которые часто упускают:
- Имя файла от пользователя нельзя писать как есть. Строка вида
..\..\config.iniвыведет запись за пределы каталога. Чистите разделители и... - Уникальный префикс защищает от перезаписи, когда два пользователя загружают
Прайс.xlsxодновременно. УдалитьИзВременногоХранилищаосвобождает память рабочего процесса сразу, не дожидаясь закрытия формы.
Сетевая папка и права службы
Если каталог — сетевой ресурс \\fileserver\1c$\incoming, запись выполняет не пользователь, а учётная запись службы сервера 1С. По умолчанию это локальная учётная запись USR1CV8, и у неё нет прав на чужие сетевые ресурсы.
Варианты решения:
- 🔑 запустить службу «Агент сервера 1С:Предприятия 8.3» от доменной учётной записи и дать ей права на запись в папку;
- 📁 писать в локальный каталог сервера, а дальше раскладывать файлы штатными средствами ОС.
Смена учётной записи службы требует её перезапуска. Все сеансы пользователей на этом сервере оборвутся. Делайте это вне рабочего времени и предупредив людей.
Отдельный случай — облачные и арендованные серверы, где включён безопасный режим или профили безопасности. Там запись на диск может быть запрещена администратором кластера, и правильный путь — хранить файл в базе.
Хранение в реквизите базы
Чтобы файл ехал вместе с базой в выгрузке .dt и не зависел от дисков, кладите его в реквизит типа ХранилищеЗначения:
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
ДокументОбъект.ФайлВложения = Новый ХранилищеЗначения(ДвоичныеДанныеФайла, Новый СжатиеДанных(9));
ДокументОбъект.Записать();
Читают обратно через ДокументОбъект.ФайлВложения.Получить(). Сжатие уровня 9 хорошо жмёт XML и текст, а для PDF, JPEG и ZIP почти ничего не даёт.
В типовых конфигурациях на БСП не изобретайте своё хранение: там есть подсистема «Работа с файлами» с присоединёнными файлами, версиями и томами на диске. Файл, загруженный через её механизмы, сразу виден в карточке объекта.
Какое хранилище выбрать
| Куда | Плюсы | Минусы |
|---|---|---|
| Каталог на сервере | База не растёт, файлы видны другим программам | Нужны права службы, файлы не попадают в .dt |
ХранилищеЗначения в реквизите | Всё в одной базе, транзакционно | База растёт, выгрузка .dt тяжелеет |
| Присоединённые файлы БСП | Версии, тома, готовый интерфейс | Только в конфигурациях на БСП |
Как обработать ошибки при передаче файла
Ошибки бывают на трёх этапах: клиент не смог прочитать файл, данные не дошли до хранилища, сервер не смог записать. Каждый ловите там, где он возникает.
На сервере оборачивайте запись в Попытка и пишите подробности в журнал регистрации, а пользователю отдавайте короткий текст:
&НаСервереБезКонтекста
Функция ЗаписатьФайлБезопасно(Адрес, ПолноеИмя)
Попытка
ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(Адрес);
ДвоичныеДанныеФайла.Записать(ПолноеИмя);
УдалитьИзВременногоХранилища(Адрес);
Возврат "";
Исключение
Инфо = ИнформацияОбОшибке();
ЗаписьЖурналаРегистрации("ЗагрузкаФайла", УровеньЖурналаРегистрации.Ошибка, , ,
ПодробноеПредставлениеОшибки(Инфо));
Возврат КраткоеПредставлениеОшибки(Инфо);
КонецПопытки;
КонецФункции
Пустая строка на выходе значит успех, непустая — текст для Сообщить на клиенте. Каталог заранее проверяйте через Новый Файл(Каталог).Существует() и создавайте СоздатьКаталог(Каталог), если его нет.
Передача файла на сервер в веб-клиенте и мобильном приложении
Браузер не даёт сайту читать диск пользователя без его участия. Поэтому в веб-клиенте 1С:
- ✅ Интерактивный выбор (диалог «Открыть файл») работает без расширения во всех поддерживаемых браузерах.
- ⛔ Помещение по известному пути (
Интерактивно = Ложь) требует расширения работы с файлами. - ⛔
Новый ДвоичныеДанные(Путь)на клиенте в веб-клиенте недоступен.
Проверка и подключение расширения:
&НаКлиенте
Процедура ПодготовитьРасширение(Команда)
Оповещение = Новый ОписаниеОповещения("ПодготовитьРасширениеЗавершение", ЭтотОбъект);
НачатьПодключениеРасширенияРаботыСФайлами(Оповещение);
КонецПроцедуры
&НаКлиенте
Процедура ПодготовитьРасширениеЗавершение(Подключено, ДополнительныеПараметры) Экспорт
Если Не Подключено Тогда
НачатьУстановкуРасширенияРаботыСФайлами();
КонецЕсли;
КонецПроцедуры
После установки браузер обычно нужно перезапустить, и только потом расширение подключится.
Пишите код так, чтобы он работал одинаково в тонком и веб-клиенте: асинхронные методы, диалог по умолчанию, неинтерактивный вариант — как опция. Тогда не придётся держать две ветки через #Если ВебКлиент Тогда.
В мобильном приложении и мобильном клиенте асинхронные методы помещения файлов тоже работают. Диалог выбора файла показывает системный выбор документа, фото или файла из облака. Произвольный путь вида C:\... там бессмыслен: доступ к файловой системе устройства ограничен песочницей приложения.
Большие файлы: как передать без зависаний и обрывов
Файл в несколько мегабайт уходит без вопросов. Проблемы начинаются с десятков и сотен мегабайт.
Что ограничивает размер
- 🌐 Веб-сервер публикации. В Apache и IIS свои лимиты на размер тела запроса и таймауты. Если перед публикацией стоит nginx, проверьте
client_max_body_size. - 🧠 Память рабочего процесса.
ДвоичныеДанныецеликом лежат в памятиrphost. Десять пользователей с файлами по 500 МБ — это пять гигабайт. - ⏱️ Время вызова. Длинная синхронная обработка на сервере держит соединение и может упереться в таймаут.
Приёмы
- Используйте
НачатьПомещениеФайлаНаСерверс оповещением о ходе выполнения. Пользователь видит прогресс и может отменить загрузку, а не закрывает «зависшее» окно. - Режьте файл на части. Глобальная функция
РазделитьФайл(ИмяФайла, РазмерЧасти)возвращает массив имён частей. Передайте части по очереди, на сервере соберите ихОбъединитьФайлы(МассивИмён, ИмяРезультата). - Сжимайте текстовые файлы. XML и CSV в ZIP-архиве (
ЗаписьZipФайла) уменьшаются в разы, на сервере архив распаковывают черезЧтениеZipФайла. - Сразу пишите на диск и удаляйте из хранилища. Не держите сотни мегабайт во временном хранилище до закрытия формы.
- Разбор — в фоне. Сервер сохранил файл, фоновое задание его обработало, форма показала результат.
Пример оповещения о ходе выполнения:
&НаКлиенте
Процедура ХодЗагрузки(ПомещаемыйФайл, Помещено, ОтказОтПомещенияФайла, ПомещеноПроцентов, ДополнительныеПараметры) Экспорт
Состояние("Загрузка файла", ПомещеноПроцентов);
КонецПроцедуры
Его передают вторым параметром: НачатьПомещениеФайлаНаСервер(Оповещение, Новый ОписаниеОповещения("ХодЗагрузки", ЭтотОбъект), , , ПараметрыДиалога, УникальныйИдентификатор). Если не уверены в составе параметров обработчика на вашей платформе, сверьтесь с синтакс-помощником по методу.
Если файл — регулярный обмен большого объёма между системами, а не ручная загрузка, файлы лучше класть в общую папку обмена, откуда их забирает регламентное задание. Ручная загрузка через форму для этого не предназначена.