ОписаниеПередаваемогоФайла.По умолчанию

Конструкторы

Задает расположение передаваемого файла на сервере и на клиенте.

Синтаксис

Новый ОписаниеПередаваемогоФайла(<Имя>, <Хранение>)
Полное описание, параметры и ограничения

ОписаниеПередаваемогоФайла.По умолчанию

ОписаниеПередаваемогоФайла (TransferableFileDescription)

По хранению и имени

Доступен, начиная с версии 8.2.

Синтаксис:
Новый ОписаниеПередаваемогоФайла(<Имя>, <Хранение>)
Параметры:

<Имя> (необязательный)
Тип: Строка.
Положение файла на клиенте.

<Хранение> (необязательный)
Тип: Строка.
Положение файла на сервере.
Описание:

Задает расположение передаваемого файла на сервере и на клиенте.

Использование в версии:

Доступен, начиная с версии 8.2.

Методическая информация

Практические примеры

Передать два разных своих файла и восстановить их без предположения о порядке

Полный пакетный цикл: два собственных источника ABC-85 и XYZ-85, реальные описания путей и результатов, проверка хранилища, удаление только своих исходников и восстановление без архива.

Где и как запускать

Полный клиентский модуль управляемой формы нового стенда УчебнаяПередачаФайлов85; толстый клиент Windows, пакетная передача своих файлов без архива и без диалога.

Подготовка

  1. Создайте новую пустую учебную конфигурацию УчебнаяПередачаФайлов85 на платформе 8.5.1.1343. Не используйте рабочие УТ, КА, УНФ, БП или копию с реальными файлами и данными.

  2. Выберите управляемое приложение и толстый клиент Windows. Код не рассчитан на веб-клиент, мобильную платформу, серверный модуль или фоновое задание.

  3. Обработки → ОпытПередачи85 → управляемая форма Форма. Для каждого урока нужна отдельная обработка либо полная замена модуля формы; не склеивайте два полных модуля.

  4. Реквизиты формы: ПодтверждаюСвоиФайлы85, Занято85, ОтменитьДоПередачи85 типа Булево; ПутьСвоегоКаталога85 и Итог85 типа Строка неограниченной длины. Исходно флажки Ложь.

  5. Согласие и ОтменитьДоПередачи85 выводите флажками для ввода; Занято85, путь и итог показывайте только для просмотра. Пути и адреса хранилища пользователь не вводит.

  6. Создайте команду с именем основной процедуры урока, назначьте одноимённое действие и кнопку. ПередЗакрытием назначьте соответствующему событию формы с четырьмя параметрами из полного модуля.

  7. Вставьте весь модуль, нажмите F7 и обновите только собственный стенд. runtimeVerifiedScenarios=0: при подготовке справочника 1С, загрузка, скачивание и очистка не запускались.

  8. Учётная запись должна иметь права работы в своём локальном временном каталоге. Администратор заранее исключает посторонних писателей и подмену файлов ссылками; общие и сетевые каталоги не используйте.

  9. ПолучитьИмяВременногоФайла создаёт только имя. Свой отдельный каталог с расширением guidefiles создаётся после проверки, что по этому имени нет существующего объекта.

  10. Исходные данные генерируются из коротких строк непосредственно кодом. Не подставляйте рабочие документы, токены, архивы, персональные данные или внешние URL.

  11. В первом ручном опыте подтвердите свой стенд и оставьте ОтменитьДоПередачи85=Ложь. Второй опыт выполняйте только после завершения первого, уже с ОтменитьДоПередачи85=Истина.

  12. Оповещение ПередСвоим… остаётся Экспорт и вызывается платформой перед загрузкой. Его возвращаемый параметр Отказ устанавливается прямо, а не через выдуманный результат процедуры.

  13. Передаётся настоящий УникальныйИдентификатор экземпляра формы. Не заменяйте его Новым УникальныйИдентификатор: произвольный UUID не становится идентификатором этой формы.

  14. Собственные известные адреса удаляются независимо от результата опыта. После исключения внутри передачи некоторые адреса могут быть неизвестны коду; время их жизни ограничено уничтожением формы, а не обещанием немедленной полной очистки.

  15. Собственные файлы удаляются только по фиксированным именам; каталог удаляется лишь после проверки его пустоты. Любые неизвестные файлы или подкаталоги остаются для ручного разбора.

  16. Основная команда и кнопка ПередатьСвоиДваФайла85. source.txt содержит ABC-85, copy.txt содержит XYZ-85, поэтому перестановка адресов обнаруживается по байтам.

  17. ПараметрыПолученияАрхиваФайлов не передаются. Учебный опыт не создаёт архив: два файла скачиваются отдельно в собственный каталог.

&НаКлиенте
Асинх Процедура ПередатьСвоиДваФайла85(Команда)
    ПроверитьСвойСтенд85();
    Занято85 = Истина;
    К = Неопределено;
    СвоиАдреса = Новый Массив;
    ОшибкаОпыта = "";
    Отчет = "";
    Попытка
        К = СоздатьСвойКонтекст85();
        СоздатьКаталог(К.Каталог);
        К.КаталогСоздан = Истина;
        ЗаписатьСвойИсходник85(К);
        ВторыеБайты = ПолучитьДвоичныеДанныеИзСтроки("XYZ-85", КодировкаТекста.UTF8);
        ВторойФайл = Новый Файл(К.Копия);
        Если ВторойФайл.Существует() Тогда
            ВызватьИсключение "Свой второй источник уже занят; перезапись запрещена";
        КонецЕсли;
        ВторыеБайты.Записать(К.Копия);
        Передаваемые = Новый Массив;
        Передаваемые.Добавить(Новый ОписаниеПередаваемогоФайла(К.Исходник));
        Передаваемые.Добавить(Новый ОписаниеПередаваемогоФайла(К.Копия));
        ПередНачалом = Новый ОписаниеОповещения("ПередСвоимиФайлами85", ЭтаФорма, К);
        Помещенные = Ждать ПоместитьФайлыНаСерверАсинх(, ПередНачалом, Передаваемые, УникальныйИдентификатор);
        Если Помещенные = Неопределено Тогда
            Отчет = "Помещение двух файлов отменено. Проверка успешной передачи не выполнялась.";
        Иначе
            Если ТипЗнч(Помещенные) <> Тип("Массив") Тогда
                ВызватьИсключение "Ожидался массив реальных результатов помещения";
            КонецЕсли;
            Для Каждого Помещенный Из Помещенные Цикл
                ЗапомнитьСвойАдрес85(Помещенный, СвоиАдреса);
            КонецЦикла;
            Если Помещенные.Количество() <> 2 Тогда
                ВызватьИсключение "Успешный опыт требует двух результатов";
            КонецЕсли;
            АдресПервого = "";
            АдресВторого = "";
            Для Каждого Помещенный Из Помещенные Цикл
                Если ТипЗнч(Помещенный) <> Тип("ОписаниеПомещенногоФайла") Тогда
                    ВызватьИсключение "В массиве нет ожидаемого описания помещения";
                КонецЕсли;
                СсылкаФайла = Помещенный.СсылкаНаФайл;
                Если ТипЗнч(СсылкаФайла) <> Тип("СсылкаНаФайл") Тогда
                    ВызватьИсключение "Результат не содержит реальной ссылки на свой файл";
                КонецЕсли;
                Если СсылкаФайла.Имя = "source.txt" И АдресПервого = "" Тогда
                    АдресПервого = ПроверитьСвоеПомещение85(Помещенный, "source.txt", К.ОжидаемыеБайты);
                ИначеЕсли СсылкаФайла.Имя = "copy.txt" И АдресВторого = "" Тогда
                    АдресВторого = ПроверитьСвоеПомещение85(Помещенный, "copy.txt", ВторыеБайты);
                Иначе
                    ВызватьИсключение "Повторное или чужое имя в результатах помещения";
                КонецЕсли;
            КонецЦикла;
            Если АдресПервого = "" Или АдресВторого = "" Или АдресПервого = АдресВторого Тогда
                ВызватьИсключение "Два разных источника требуют двух различных подтверждённых адресов";
            КонецЕсли;
            ПроверитьСвоюОбласть85(К);
            ПервыйФайл = Новый Файл(К.Исходник);
            Если Не ПервыйФайл.Существует() Или ПервыйФайл.ЭтоКаталог()
                Или Не ВторойФайл.Существует() Или ВторойФайл.ЭтоКаталог() Тогда
                ВызватьИсключение "Перед удалением нужны два собственных файла, не каталоги";
            КонецЕсли;
            УдалитьФайлы(К.Исходник);
            УдалитьФайлы(К.Копия);
            Если ПервыйФайл.Существует() Или ВторойФайл.Существует() Тогда
                ВызватьИсключение "Свои назначения не свободны после удаления источников";
            КонецЕсли;
            Получаемые = Новый Массив;
            Получаемые.Добавить(Новый ОписаниеПередаваемогоФайла(К.Исходник, АдресПервого));
            Получаемые.Добавить(Новый ОписаниеПередаваемогоФайла(К.Копия, АдресВторого));
            Полученные = Ждать ПолучитьФайлыССервераАсинх(Получаемые, К.Каталог);
            Если ТипЗнч(Полученные) <> Тип("Массив") Или Полученные.Количество() <> 2 Тогда
                ВызватьИсключение "Без диалога требуются два реальных результата получения";
            КонецЕсли;
            ПроверенПервый = Ложь;
            ПроверенВторой = Ложь;
            Для Каждого Полученный Из Полученные Цикл
                Если ТипЗнч(Полученный) <> Тип("ОписаниеПереданногоФайла") Тогда
                    ВызватьИсключение "В массиве не ОписаниеПереданногоФайла";
                КонецЕсли;
                Если Полученный.Имя = "source.txt" И Не ПроверенПервый Тогда
                    Отчет = Отчет + " " + ПроверитьСвоеПолучение85(Полученный, К.Исходник, АдресПервого, К.ОжидаемыеБайты);
                    ПроверенПервый = Истина;
                ИначеЕсли Полученный.Имя = "copy.txt" И Не ПроверенВторой Тогда
                    Отчет = Отчет + " " + ПроверитьСвоеПолучение85(Полученный, К.Копия, АдресВторого, ВторыеБайты);
                    ПроверенВторой = Истина;
                Иначе
                    ВызватьИсключение "Повторное или чужое имя в результатах получения";
                КонецЕсли;
            КонецЦикла;
            Если Не ПроверенПервый Или Не ПроверенВторой Тогда
                ВызватьИсключение "Не подтверждены оба своих полученных файла";
            КонецЕсли;
            ПроверитьСвойПоиск85(НайтиФайлы(К.Каталог, "*.txt", Ложь), К);
            Отчет = "Оба файла восстановлены из временного хранилища: ABC-85 и XYZ-85; байты совпали." + Отчет;
        КонецЕсли;
        Если К.ПричинаОтказа <> "" Тогда
            Отчет = Отчет + " " + К.ПричинаОтказа;
        КонецЕсли;
    Исключение
        ОшибкаОпыта = ОписаниеОшибки();
    КонецПопытки;
    ЗавершитьСвоюПередачу85(К, СвоиАдреса, ОшибкаОпыта, Отчет);
КонецПроцедуры

&НаКлиенте
Процедура ПередСвоимиФайлами85(ПомещаемыеФайлы, ОтказОтПомещенияВсехФайлов, К) Экспорт
    Если ТипЗнч(ПомещаемыеФайлы) <> Тип("Массив") Или ПомещаемыеФайлы.Количество() <> 2 Тогда
        ОтказОтПомещенияВсехФайлов = Истина;
        К.ПричинаОтказа = "Перед началом не получены две собственные ссылки";
        Возврат;
    КонецЕсли;
    Для Каждого СсылкаФайла Из ПомещаемыеФайлы Цикл
        Если ТипЗнч(СсылкаФайла) <> Тип("СсылкаНаФайл") Тогда
            ОтказОтПомещенияВсехФайлов = Истина;
            К.ПричинаОтказа = "Перед началом обнаружен неверный тип ссылки";
            Возврат;
        КонецЕсли;
        Если СсылкаФайла.Имя <> "source.txt" И СсылкаФайла.Имя <> "copy.txt" Тогда
            ОтказОтПомещенияВсехФайлов = Истина;
            К.ПричинаОтказа = "К передаче предложено не своё имя";
            Возврат;
        КонецЕсли;
    КонецЦикла;
    Если К.Отменить Тогда
        ОтказОтПомещенияВсехФайлов = Истина;
        К.ПричинаОтказа = "Контролируемая отмена до помещения двух собственных файлов";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ЗапомнитьСвойАдрес85(Помещенный, СвоиАдреса)
    Если ТипЗнч(Помещенный) = Тип("ОписаниеПомещенногоФайла") Тогда
        Адрес = Помещенный.Адрес;
        Если ТипЗнч(Адрес) = Тип("Строка") И Адрес <> "" Тогда
            Если СвоиАдреса.Найти(Адрес) = Неопределено Тогда
                СвоиАдреса.Добавить(Адрес);
            КонецЕсли;
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Функция ПроверитьСвоеПомещение85(Помещенный, Имя, ОжидаемыеБайты)
    Если ТипЗнч(Помещенный) <> Тип("ОписаниеПомещенногоФайла") Тогда
        ВызватьИсключение "Нужно реальное описание результата помещения";
    КонецЕсли;
    Если Помещенный.ПомещениеФайлаОтменено Тогда
        ВызватьИсключение "Этот собственный файл не был помещён";
    КонецЕсли;
    Адрес = Помещенный.Адрес;
    Если ТипЗнч(Адрес) <> Тип("Строка") Или Адрес = "" Тогда
        ВызватьИсключение "Нет реального адреса своего результата";
    КонецЕсли;
    СсылкаФайла = Помещенный.СсылкаНаФайл;
    Если ТипЗнч(СсылкаФайла) <> Тип("СсылкаНаФайл") Тогда
        ВызватьИсключение "Нет реальной ссылки на свой помещённый файл";
    КонецЕсли;
    Если СсылкаФайла.Имя <> Имя Тогда
        ВызватьИсключение "Ссылка результата относится к другому имени";
    КонецЕсли;
    ДанныеХранилища = ПолучитьИзВременногоХранилища(Адрес);
    Если ТипЗнч(ДанныеХранилища) <> Тип("ДвоичныеДанные") Тогда
        ВызватьИсключение "Во временном хранилище ожидались свои двоичные данные";
    КонецЕсли;
    Если Base64Строка(ДанныеХранилища) <> Base64Строка(ОжидаемыеБайты) Тогда
        ВызватьИсключение "Байты во временном хранилище не совпали со своим источником";
    КонецЕсли;
    Возврат Адрес;
КонецФункции

&НаКлиенте
Функция ПроверитьСвоеПолучение85(Полученный, Путь, Адрес, ОжидаемыеБайты)
    Если ТипЗнч(Полученный) <> Тип("ОписаниеПереданногоФайла") Тогда
        ВызватьИсключение "Без диалога требуется реальный результат ОписаниеПереданногоФайла";
    КонецЕсли;
    Если ТипЗнч(Полученный.ИдентификаторФайла) <> Тип("УникальныйИдентификатор") Тогда
        ВызватьИсключение "Не получен идентификатор переданного файла";
    КонецЕсли;
    ФайлНазначения = Новый Файл(Путь);
    Если Полученный.Имя <> ФайлНазначения.Имя
        Или Полученный.ПолноеИмя <> Путь Или Полученный.Хранение <> Адрес Тогда
        ВызватьИсключение "Описание получения не совпало со своим назначением и хранилищем";
    КонецЕсли;
    Если Не ФайлНазначения.Существует() Или ФайлНазначения.ЭтоКаталог() Тогда
        ВызватьИсключение "Реальный собственный файл после получения не найден";
    КонецЕсли;
    ПолученныеБайты = Новый ДвоичныеДанные(Путь);
    Если Base64Строка(ПолученныеБайты) <> Base64Строка(ОжидаемыеБайты) Тогда
        ВызватьИсключение "Полученный файл отличается от своих исходных байтов";
    КонецЕсли;
    Возврат Полученный.Имя + ": " + Строка(Полученный.ИдентификаторФайла);
КонецФункции

&НаКлиенте
Функция ОчиститьСвоиАдреса85(СвоиАдреса)
    ОшибкаОчистки = "";
    Для Каждого Адрес Из СвоиАдреса Цикл
        Попытка
            УдалитьИзВременногоХранилища(Адрес);
        Исключение
            ОшибкаОчистки = ОшибкаОчистки + " " + ОписаниеОшибки();
        КонецПопытки;
    КонецЦикла;
    Возврат ОшибкаОчистки;
КонецФункции

&НаКлиенте
Процедура ЗавершитьСвоюПередачу85(К, СвоиАдреса, ОшибкаОпыта, Отчет)
    ОшибкаХранилища = ОчиститьСвоиАдреса85(СвоиАдреса);
    ОшибкаФайлов = ОчиститьТолькоСвойОпыт85(К);
    Занято85 = Ложь;
    Если ОшибкаОпыта <> "" Или ОшибкаХранилища <> "" Или ОшибкаФайлов <> "" Тогда
        Итог85 = ОшибкаОпыта + " " + ОшибкаХранилища + " " + ОшибкаФайлов;
        ВызватьИсключение Итог85;
    КонецЕсли;
    Итог85 = Отчет + " Свои известные адреса удалены; свои файлы и пустой каталог удалены.";
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьСвойСтенд85()
    Если Не ПодтверждаюСвоиФайлы85 Тогда
        ВызватьИсключение "Только собственный новый стенд УчебнаяПередачаФайлов85 с согласием";
    КонецЕсли;
    Если Занято85 Тогда
        ВызватьИсключение "Дождитесь завершения текущего собственного опыта";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Функция СоздатьСвойКонтекст85()
    Разделитель = ПолучитьРазделительПути();
    Каталог = ПолучитьИмяВременногоФайла("guidefiles");
    ФайлКаталога = Новый Файл(Каталог);
    Если ФайлКаталога.Существует() Тогда
        ВызватьИсключение "Уникальное имя уже занято; найденный объект не считается своим";
    КонецЕсли;
    К = Новый Структура;
    К.Вставить("Каталог", Каталог);
    К.Вставить("Разделитель", Разделитель);
    К.Вставить("КаталогСоздан", Ложь);
    К.Вставить("Исходник", Каталог + Разделитель + "source.txt");
    К.Вставить("Копия", Каталог + Разделитель + "copy.txt");
    К.Вставить("Перемещенный", Каталог + Разделитель + "moved.txt");
    К.Вставить("ОжидаемыеБайты", ПолучитьДвоичныеДанныеИзСтроки("ABC-85", КодировкаТекста.UTF8));
    К.Вставить("ПричинаОтказа", "");
    К.Вставить("Отменить", ОтменитьДоПередачи85);
    ПутьСвоегоКаталога85 = Каталог;
    Возврат К;
КонецФункции

&НаКлиенте
Процедура ПроверитьСвоюОбласть85(К)
    Если К.Каталог <> ПутьСвоегоКаталога85 Или Прав(К.Каталог, 11) <> ".guidefiles" Тогда
        ВызватьИсключение "Не совпал собственный сгенерированный каталог этого опыта";
    КонецЕсли;
    Если К.Исходник <> К.Каталог + К.Разделитель + "source.txt"
        Или К.Копия <> К.Каталог + К.Разделитель + "copy.txt"
        Или К.Перемещенный <> К.Каталог + К.Разделитель + "moved.txt" Тогда
        ВызватьИсключение "Изменились границы собственной файловой области";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ЗаписатьСвойИсходник85(К)
    ПроверитьСвоюОбласть85(К);
    ФайлКаталога = Новый Файл(К.Каталог);
    Если Не К.КаталогСоздан Или Не ФайлКаталога.Существует() Или Не ФайлКаталога.ЭтоКаталог() Тогда
        ВызватьИсключение "Создание своего каталога ещё не подтверждено";
    КонецЕсли;
    ФайлИсходника = Новый Файл(К.Исходник);
    Если ФайлИсходника.Существует() Тогда
        ВызватьИсключение "Свой источник не должен перезаписывать существующий файл";
    КонецЕсли;
    К.ОжидаемыеБайты.Записать(К.Исходник);
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьСвойПоиск85(Найденные, К)
    ПроверитьСвоюОбласть85(К);
    Если ТипЗнч(Найденные) <> Тип("Массив") Или Найденные.Количество() <> 2 Тогда
        ВызватьИсключение "После получения нужны два реальных собственных txt-файла";
    КонецЕсли;
    Для Каждого Найденный Из Найденные Цикл
        Если Найденный.ПолноеИмя <> К.Исходник И Найденный.ПолноеИмя <> К.Копия Тогда
            ВызватьИсключение "Поиск вернул неожиданный объект; не удаляйте его";
        КонецЕсли;
        Если Найденный.ЭтоКаталог() Тогда
            ВызватьИсключение "Вместо собственного txt-файла найден каталог";
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

&НаКлиенте
Функция ОчиститьТолькоСвойОпыт85(К)
    Если К = Неопределено Тогда
        Возврат "";
    КонецЕсли;
    ОшибкаОчистки = "";
    Попытка
        ПроверитьСвоюОбласть85(К);
    Исключение
        Возврат "Границы опыта не подтвердились; очистка не начата: " + ОписаниеОшибки();
    КонецПопытки;
    Если Не К.КаталогСоздан Тогда
        Возврат "Создание каталога не подтвердилось; автоматическая очистка не выполнялась";
    КонецЕсли;
    Имена = Новый Массив;
    Имена.Добавить(К.Исходник);
    Имена.Добавить(К.Копия);
    Имена.Добавить(К.Перемещенный);
    Для Каждого Имя Из Имена Цикл
        Попытка
            СвойФайл = Новый Файл(Имя);
            Если СвойФайл.Существует() Тогда
                Если СвойФайл.ЭтоКаталог() Тогда
                    ВызватьИсключение "На месте своего файла обнаружен каталог; его не удаляем";
                КонецЕсли;
                УдалитьФайлы(Имя);
            КонецЕсли;
            Если СвойФайл.Существует() Тогда
                ВызватьИсключение "Свой файл остался после удаления";
            КонецЕсли;
        Исключение
            ОшибкаОчистки = ОшибкаОчистки + " " + ОписаниеОшибки();
        КонецПопытки;
    КонецЦикла;
    Попытка
        СвойКаталог = Новый Файл(К.Каталог);
        Если СвойКаталог.Существует() Тогда
            Остаток = НайтиФайлы(К.Каталог, "*", Ложь);
            Если Остаток.Количество() <> 0 Тогда
                ВызватьИсключение "Каталог не пуст; неизвестные объекты не удаляем";
            КонецЕсли;
            УдалитьФайлы(К.Каталог);
        КонецЕсли;
        Если СвойКаталог.Существует() Тогда
            ВызватьИсключение "Свой пустой каталог остался после удаления";
        КонецЕсли;
    Исключение
        ОшибкаОчистки = ОшибкаОчистки + " " + ОписаниеОшибки();
    КонецПопытки;
    Возврат ОшибкаОчистки;
КонецФункции

&НаКлиенте
Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка)
    Если Занято85 Тогда
        Отказ = Истина;
        ТекстПредупреждения = "Дождитесь завершения собственного файлового опыта";
    КонецЕсли;
КонецПроцедуры

Разбор по шагам

  1. Создаются только два собственных источника с разными шестибайтными значениями. Второй файл не является копией первого несмотря на имя copy.txt.

  2. Новый ОписаниеПередаваемогоФайла принимает первым аргументом клиентское имя/путь, вторым — хранение. Для помещения задаётся только путь, чтобы новый адрес создала платформа.

  3. Перед началом callback получает массив реальных СсылкаНаФайл и возвращаемый ОтказОтПомещенияВсехФайлов. Контролируемая отмена отклоняет весь пакет до передачи.

  4. Ждать ПоместитьФайлыНаСерверАсинх возвращает массив описаний либо Неопределено. Сначала собираются все доступные реальные адреса для очистки, затем проверяется комплект из двух результатов.

  5. source.txt и copy.txt сопоставляются по имени из реальной ссылки, а не по индексам массива. Дубликаты, чужие имена, одинаковые адреса и неправильные байты в хранилище отклоняются.

  6. Собственные исходники удаляются только после подтверждения обоих сохранённых содержимых. Назначения проверяются на отсутствие; теперь совпадение скачанных байтов нельзя объяснить оставшимися исходниками.

  7. Два новых ОписаниеПередаваемогоФайла содержат абсолютные свои пути и собственные подтверждённые адреса. ПолучитьФайлыССервераАсинх получает строковый базовый каталог, выбирая вариант без диалога; параметр архива не передан.

  8. Каждый реальный результат получения вновь сопоставляется по имени. Проверяются свойства, существование и содержимое обоих файлов; дубликат не может заменить недостающий второй результат.

  9. Независимая очистка известного хранилища и своей фиксированной файловой области выполняется после всех ветвей. Ошибки сохраняются, занятость снимается; частичный результат не называется полным успехом.

Как проверить результат

  1. Успех требует source.txt=ABC-85 и copy.txt=XYZ-85 как в хранилище, так и после получения; одинаковые байты для обоих файлов недопустимы.

  2. Порядок массивов не используется: в коде нет Помещенные[0] или Полученные[0]. Каждый комплект проверяется по именам и наличию обеих позиций.

  3. После подтверждённой загрузки исходники удаляются до скачивания. Отсутствие обеих целей проверяется отдельно от проверки количества описаний.

  4. ОтменитьДоПередачи85=Истина отклоняет весь пакет до начала помещения и не запускает получение.

  5. Никакой архив и никакой внешний URL не создаются; после штатного завершения свои файлы и пустой каталог отсутствуют.

Ожидаемый результат

При собственном ручном успешном запуске оба разных содержимых восстановлены из своих адресов, независимо от порядка результатов; отмена или частичный результат не выдаются за успех.

На что обратить внимание

  1. Адрес временного хранилища — не путь на диске сервера. Локальный путь клиента также не становится доступным серверу; передаются байты, а не право читать клиентский каталог.

  2. ПолучитьИзВременногоХранилища и УдалитьИзВременногоХранилища выполняют обращения к серверу даже из клиентского кода. Это не настоящая сеть к внешнему API; во время подготовки никакие обращения в 1С не выполнялись.

  3. Без идентификатора формы данные могут исчезнуть при следующем серверном запросе. Здесь используется реальный идентификатор формы и явная очистка известных адресов; закрытие/уничтожение формы — запасная граница жизни хранилища.

  4. Помещение может вернуть Неопределено при отмене. Если получено описание с ПомещениеФайлаОтменено=Истина, оно тоже не подтверждает успешную передачу.

  5. Вариант получения с диалогом возвращает Неопределено и не доказывает, что пользователь сохранил файл. Здесь применён вариант без диалога с проверкой реального описания и файла.

  6. ОписаниеПереданногоФайла получается только из реального результата API; Новый ОписаниеПереданногоФайла здесь недопустим. Его четыре используемых свойства только читаются.

  7. ИдентификаторФайла является УникальныйИдентификатор, а не доказательством совпадения содержимого. Байты дополнительно проверяются; равенство идентификаторов загрузки и скачивания не предполагается.

  8. Уникальное имя и Существует не являются атомарным резервированием пути. Проверки годятся для закрытого учебного каталога без стороннего вмешательства, а не для враждебной файловой среды.

  9. УдалитьФайлы может рекурсивно удалить каталог. Нельзя менять известные файловые имена на чужие пути или удалять непустой каталог; проверка ЭтоКаталог и проверка пустоты обязательны.

  10. Base64 сравнивает реальные байты, но расходует дополнительную память. Этот учебный подход рассчитан на шесть байт, а не гигабайтные выгрузки.

  11. ПередЗакрытием блокирует обычное закрытие занятой формы, но не отключение питания и принудительное завершение клиента. Гарантий очистки после аварии нет.

  12. Файлы здесь создаются только будущим ручным запуском владельца в собственном стенде. Это не резервное копирование данных; ни новых резервных копий, ни архивов справочника при подготовке не создаётся.

  13. Этот код не даёт production-разрешение удалять адреса, полученные от недоверенного клиента. Он работает только с результатами собственного опыта в новой пустой базе и не является готовым сервисом загрузки для других пользователей.

  14. При частичном исключении массив результатов может не вернуться. Код не выдумывает адреса для очистки: уничтожение формы ограничивает срок хранения неизвестных результатов.

  15. Имя файла здесь уникально внутри пакета ровно из двух своих имён. Для общего массового обмена нужны явная модель корреляции и правила коллизий, а не это учебное сопоставление по имени.

Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.

Выгрузка учебных счетов: один ZIP и два обычных JSON, исходники 2500

Понять различие двух конструкторов и режимов получения архива на реальной выгрузке собственных данных, проверяя полные байты и неизмененный источник.

Где и как запускать

Windows 8.5.1.1343; собственная новая File CF; тонкий клиент Taxi. MAIN управляемой формы: server-create2, client transfer/callback2; серверный общий модуль получает только UUID и фиксированные скалярные источники.

Подготовка

  1. Это будущий самостоятельный ручной опыт, а не выполненная здесь выгрузка. На Windows 8.5.1.1343 создайте новую собственную файловую базу C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\IB. Запускайте только тонким клиентом с интерфейсом Такси, одним своим сеансом. Не используйте рабочую базу, сетевой каталог, расширения, фоновые задания или чужие файлы.

  2. Конфигурация УчебнаяВыгрузкаСчетов85: ОсновнойРежимЗапуска=УправляемоеПриложение, совместимость НеИспользовать, язык Русский. Создайте только форму Счета85, общую команду ВыгрузитьСчета85, подсистему УчебныеСчета85, общий модуль УчебныйАрхивСервер85 и роль УчебнаяПолная85. Документы, справочники, регистры и сервисы здесь не нужны. Никаких исходных данных в базе не записывается.

  3. Создайте одного пользователя УченикАрхива85 с собственной полной ролью, включая Администрирование: ПолучитьПользователей требует именно этих прав. Это ручная подготовка новой базы, не изменение чужих учетных записей или доказательство прав по имени роли.

  4. Вручную создайте только для этого нового стенда папку C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output и три пустые подпапки Always, Raw, Extract. Все каталоги должны быть обычными собственными локальными каталогами без переадресаций/ссылок и других процессов, изменяющих файлы. Не используйте Downloads или Temp. В них не должно быть никаких существующих файлов; код проверит пустоту, но не создаст, не удалит и не перезапишет их.

  5. Общие → Общие формы → Добавить: управляемая форма Счета85. КоличествоИсточников85/Фаза85 — Число 1.0; СуммаИсточников85/РазмерИсточников85 — Число 9.0; Состояние85 — Строка 500; Готово85/Занят85/Остановка85/Завершено85 — Булево. Основного реквизита-объекта, параметров, таблиц и команд формы нет. Перенесите на форму пять полей с именами одноименных реквизитов: количество, сумма, размер, фаза, состояние. ПутьКДанным — одноименный реквизит, ТолькоПросмотр=Истина. Флаги оставьте служебными.

  6. Вставьте полный FORM в Общие → Общие формы → Счета85 → Модуль. В свойствах формы свяжите ПриСозданииНаСервере с одноименной процедурой (Отказ, СтандартнаяОбработка). Переменные источников/параметров/журнала объявлены &НаКлиенте. ПутьКДанным проверяется только сервером формы; клиент не читает вымышленное Значение элемента.

  7. Общие → Общие модули → УчебныйАрхивСервер85: Сервер=Да, Вызов сервера=Да; оба клиентских флага, Внешнее соединение, Глобальный и Привилегированный=Нет, повторное использование возвращаемых значений НеИспользовать. Вставьте COMMON целиком. Он из фиксированных собственных Base64 создает 33/33/34 байта и помещает их во временное хранилище с фактическим UUID формы, а не с новым случайным UUID. Серверу не передаются форма, команда или оповещение.

  8. Общие → Общие команды → Добавить: ВыгрузитьСчета85, без параметра, ИзменяетДанные=Нет, группа Панель действий → Сервис. Вставьте COMMAND целиком; точная предопределенная точка входа ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды), &НаКлиенте. Команда не пишет информационную базу, но ее выполнение означает ваше отдельное явное согласие создать только указанные учебные файлы.

  9. Общие → Подсистемы: УчебныеСчета85, ВключатьВКомандныйИнтерфейс=Да, Подсистема с одной командой=Нет; в Состав включите ВыгрузитьСчета85. В редакторе подсистемы Прочее → Командный интерфейс → Панель действий → Сервис включите общую видимость команды и видимость роли УчебнаяПолная85. В модуль управляемого приложения вставьте APPLICATION целиком.

  10. Обновите только собственную учебную CF и запустите тонким клиентом. Настоящий запуск0 создаст и откроет уникальную форму: 3 источника, сумма 2500, размер 100 байт, фаза 0. Открыть — процедура; отдельно читается Открыта(). Никакие файлы еще не выгружаются. Если вы не согласны создавать учебные результаты, не выполняйте команду.

  11. Проверьте все три пустые папки и выполните свою команду ВыгрузитьСчета85 один раз. Фаза 1 читает defaults конструктора, задает always.zip/ПолучатьАрхивВсегда и передает один A500. Даже один файл при Always должен стать архивом Always\always.zip. После настоящего callback архив читается актуальным ЧтениеФайлаАрхива, проверяется единственная собственная запись и извлекается в свой пустой Extract. Это не средство безопасной обработки чужого ZIP: не подменяйте созданный файл и не запускайте параллельные изменения.

  12. После подтвержденной первой фазы private routine запускает фазу 2: конструктор с именем when-required.zip и режимом ПолучатьАрхивПриНеобходимости, два источника B750/C1250. Именно в тонком клиенте native режим не архивирует: ожидаются Raw\invoice-b.json и Raw\invoice-c.json, никакого when-required.zip. Условия веб-клиента без расширения отличаются; данный урок не запускайте в веб-клиенте.

  13. Callback ПослеПолучения85 имеет два параметра: настоящий массив ОписаниеПереданногоФайла и дополнительные параметры собственного уведомления. Мы записываем реальные Имя/ПолноеИмя, но не угадываем, сколько описаний вернет упаковка и как они сопоставлены ZIP. Итог проверяется независимо по своим заранее отсутствовавшим путям, размеру и полным байтам, а все три серверных источника снова сверяются: 2×250=500, 3×250=750, 5×250=1250, всего 2500 и 100 байт.

  14. Это вариант Без диалога. Native здесь не объявляет Неопределено как отмену: неизвестное завершение не является ни отменой, ни успехом. После ошибки остается STOP и возможный частичный вывод. Не повторяйте запрос, не удаляйте файлы и не обещайте откат. Чтение архива закрывается одной попыткой; при неудачном закрытии сохраняются исходная и дополнительная диагностики, состояние ресурса неизвестно. При отсутствии callback Busy остается незавершенным; расследуйте только свой стенд вручную.

  15. Временное хранилище связано с настоящим UUID формы и живет по native правилам, а не между сеансами. Закрытие формы/сеанса во время выгрузки не является проверенным способом отмены. Для нового опыта нужен новый отдельно подготовленный собственный стенд/пустые каталоги, а не автоматическая очистка или сброс STOP.

  16. Здесь выполнено только авторство и статические проверки исходников. Компиляция 1С, GUI, доставка callback, упаковка, извлечение, файловые чтения/создание и отображение чисел не запускались. Ожидаемый результат — будущая ручная проверка, не заявление об успешном runtime.

&НаКлиенте
Перем Источники85, СнимокИсточников85, ПараметрыАрхива85, ЖурналЗавершений85;

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    Отказ = Истина;
    Попытка
        УчебныйАрхивСервер85.ПроверитьСтенд85();
        КоличествоИсточников85 = 3;
        СуммаИсточников85 = 2500;
        РазмерИсточников85 = 100;
        Фаза85 = 0;
        Готово85 = Истина;
        Занят85 = Ложь;
        Остановка85 = Ложь;
        Завершено85 = Ложь;
        Состояние85 = "Готово: по команде будут созданы только собственные учебные файлы";
        ПроверитьСвязи85();
        Отказ = Ложь;
    Исключение
        Остановка85 = Истина;
        ВызватьИсключение;
    КонецПопытки;
КонецПроцедуры

&НаСервере
Процедура ПроверитьСвязи85()
    УчебныйАрхивСервер85.ПроверитьСтенд85();
    Имена = Новый Массив;
    Имена.Добавить("КоличествоИсточников85");
    Имена.Добавить("СуммаИсточников85");
    Имена.Добавить("РазмерИсточников85");
    Имена.Добавить("Фаза85");
    Имена.Добавить("Состояние85");
    Для Каждого ИмяПоля Из Имена Цикл
        Поле = ЭтаФорма.Элементы.Найти(ИмяПоля);
        Если Поле = Неопределено Или ТипЗнч(Поле) <> Тип("ПолеФормы") Тогда
            ВызватьИсключение "Не найдены свои readonly поля формы";
        КонецЕсли;
        Если Поле.ПутьКДанным <> ИмяПоля Или Не Поле.ТолькоПросмотр Тогда
            ВызватьИсключение "Неверные связи или редактируемые поля собственных счетов";
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьГотовность85() Экспорт
    Если Готово85 <> Истина Или Остановка85 <> Ложь Или Не ЭтаФорма.Открыта()
        Или ЭтаФорма.ВладелецФормы <> Неопределено
        Или КоличествоИсточников85 <> 3 Или СуммаИсточников85 <> 2500 Или РазмерИсточников85 <> 100 Тогда
        ВызватьИсключение "Нужна собственная открытая форма с неизмененным итогом 2500 и 100 байт";
    КонецЕсли;
    ПроверитьСвязи85();
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьПустойКаталог85(Путь)
    Если Путь <> "C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Always"
        И Путь <> "C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Raw"
        И Путь <> "C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Extract" Тогда
        ВызватьИсключение "Разрешены только три собственных каталога учебного UUID-стенда";
    КонецЕсли;
    Каталог = Новый Файл(Путь);
    Если Не Каталог.Существует() Тогда
        ВызватьИсключение "Вручную подготовьте новый собственный пустой каталог";
    КонецЕсли;
    Если Не Каталог.ЭтоКаталог() Или НайтиФайлы(Путь, "*", Ложь).Количество() <> 0 Тогда
        ВызватьИсключение "Каталог должен быть пустым: ничего не перезаписываем и не удаляем";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура НачатьВыгрузку85() Экспорт
    Попытка
        ПроверитьГотовность85();
        Если Занят85 <> Ложь Или Завершено85 <> Ложь Или Фаза85 <> 0
            Или Источники85 <> Неопределено Или СнимокИсточников85 <> Неопределено Тогда
            ВызватьИсключение "Один собственный сеанс допускает только один полный опыт";
        КонецЕсли;
        ПроверитьПустойКаталог85("C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Always");
        ПроверитьПустойКаталог85("C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Raw");
        ПроверитьПустойКаталог85("C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Extract");
        Если ТипЗнч(ЭтаФорма.УникальныйИдентификатор) <> Тип("УникальныйИдентификатор") Тогда
            ВызватьИсключение "Форма должна иметь свой настоящий UUID";
        КонецЕсли;
        Источники85 = УчебныйАрхивСервер85.СоздатьИсточники85(ЭтаФорма.УникальныйИдентификатор);
        СнимокИсточников85 = Источники85;
        ЖурналЗавершений85 = Новый Массив;
        ПроверитьПолныйИсточник85();
        НачатьФазу85(1);
    Исключение
        Остановить85();
        ВызватьИсключение;
    КонецПопытки;
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьПолныйИсточник85()
    ПроверитьГотовность85();
    Если ТипЗнч(Источники85) <> Тип("ФиксированныйМассив")
        Или ТипЗнч(СнимокИсточников85) <> Тип("ФиксированныйМассив")
        Или Источники85.Количество() <> 3 Или СнимокИсточников85.Количество() <> 3 Тогда
        ВызватьИсключение "Потерян полный фиксированный источник";
    КонецЕсли;
    Для Номер = 0 По 2 Цикл
        Источник = Источники85[Номер];
        Снимок = СнимокИсточников85[Номер];
        Если ТипЗнч(Источник) <> Тип("ФиксированнаяСтруктура") Или Источник.Количество() <> 7
            Или ТипЗнч(Снимок) <> Тип("ФиксированнаяСтруктура") Или Снимок.Количество() <> 7 Тогда
            ВызватьИсключение "Не совпала структура собственного снимка";
        КонецЕсли;
        Для Каждого ПолеСнимка Из Снимок Цикл
            Если Не Источник.Свойство(ПолеСнимка.Ключ, ЗначениеПоля)
                Или ТипЗнч(ЗначениеПоля) <> ТипЗнч(ПолеСнимка.Значение)
                Или ЗначениеПоля <> ПолеСнимка.Значение Тогда
                ВызватьИсключение "Изменено одно из семи полей источника";
            КонецЕсли;
        КонецЦикла;
    КонецЦикла;
    УчебныйАрхивСервер85.ПроверитьИсточники85(Источники85);
КонецПроцедуры

&НаКлиенте
Процедура НачатьФазу85(Фаза)
    ПроверитьПолныйИсточник85();
    Если Занят85 <> Ложь Или (Фаза <> 1 И Фаза <> 2) Или Фаза85 <> Фаза - 1 Тогда
        ВызватьИсключение "Нарушена последовательность двух собственных выгрузок";
    КонецЕсли;
    Файлы = Новый Массив;
    Если Фаза = 1 Тогда
        ПараметрыАрхива85 = Новый ПараметрыПолученияАрхиваФайлов();
        Если ТипЗнч(ПараметрыАрхива85) <> Тип("ПараметрыПолученияАрхиваФайлов")
            Или ПараметрыАрхива85.ИмяАрхива <> ""
            Или ПараметрыАрхива85.Режим <> РежимПолученияАрхиваФайлов.ПолучатьАрхивВсегда Тогда
            ВызватьИсключение "Не подтверждены документированные значения конструктора по умолчанию";
        КонецЕсли;
        ПараметрыАрхива85.ИмяАрхива = "always.zip";
        ПараметрыАрхива85.Режим = РежимПолученияАрхиваФайлов.ПолучатьАрхивВсегда;
        КаталогФазы = "C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Always";
        Первый = 0;
        Последний = 0;
    Иначе
        ПараметрыАрхива85 = Новый ПараметрыПолученияАрхиваФайлов("when-required.zip", РежимПолученияАрхиваФайлов.ПолучатьАрхивПриНеобходимости);
        Если ТипЗнч(ПараметрыАрхива85) <> Тип("ПараметрыПолученияАрхиваФайлов")
            Или ПараметрыАрхива85.ИмяАрхива <> "when-required.zip"
            Или ПараметрыАрхива85.Режим <> РежимПолученияАрхиваФайлов.ПолучатьАрхивПриНеобходимости Тогда
            ВызватьИсключение "Не подтверждены имя и режим второго конструктора";
        КонецЕсли;
        КаталогФазы = "C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Raw";
        Первый = 1;
        Последний = 2;
    КонецЕсли;
    ПроверитьПустойКаталог85(КаталогФазы);
    Для Номер = Первый По Последний Цикл
        Источник = Источники85[Номер];
        ОписаниеФайла = Новый ОписаниеПередаваемогоФайла(Источник.Имя, Источник.Адрес);
        Если ТипЗнч(ОписаниеФайла) <> Тип("ОписаниеПередаваемогоФайла")
            Или ОписаниеФайла.Имя <> Источник.Имя Или ОписаниеФайла.Хранение <> Источник.Адрес Тогда
            ВызватьИсключение "Перепутаны клиентское имя и серверное хранение";
        КонецЕсли;
        Файлы.Добавить(ОписаниеФайла);
    КонецЦикла;
    Если Файлы.Количество() <> Фаза Тогда
        ВызватьИсключение "В первой фазе нужен один JSON, во второй — два";
    КонецЕсли;
    Если Фаза = 1 И (ПараметрыАрхива85.ИмяАрхива <> "always.zip"
        Или ПараметрыАрхива85.Режим <> РежимПолученияАрхиваФайлов.ПолучатьАрхивВсегда) Тогда
        ВызватьИсключение "Изменена явная политика единственного ZIP";
    КонецЕсли;
    Фаза85 = Фаза;
    Занят85 = Истина;
    Состояние85 = "Ожидаем реальное завершение фазы " + Строка(Фаза);
    ДополнительныеПараметры = Новый ФиксированнаяСтруктура("Форма,Фаза", ЭтаФорма.УникальныйИдентификатор, Фаза);
    Оповещение = Новый ОписаниеОповещения("ПослеПолучения85", ЭтаФорма, ДополнительныеПараметры);
    Если ТипЗнч(Оповещение.Модуль) <> Тип("ФормаКлиентскогоПриложения") Или Оповещение.Модуль <> ЭтаФорма Тогда
        ВызватьИсключение "Уведомление должно вернуться в свою управляемую форму";
    КонецЕсли;
    НачатьПолучениеФайловССервера(Оповещение, Файлы, КаталогФазы, ПараметрыАрхива85);
КонецПроцедуры

&НаКлиенте
Процедура ПослеПолучения85(ПолученныеФайлы, ДополнительныеПараметры) Экспорт
    Попытка
        ПроверитьПолныйИсточник85();
        Если Занят85 <> Истина Или (Фаза85 <> 1 И Фаза85 <> 2)
            Или ТипЗнч(ДополнительныеПараметры) <> Тип("ФиксированнаяСтруктура")
            Или ДополнительныеПараметры.Количество() <> 2
            Или Не ДополнительныеПараметры.Свойство("Форма", ИдентификаторФормы)
            Или Не ДополнительныеПараметры.Свойство("Фаза", ЗавершеннаяФаза)
            Или ТипЗнч(ИдентификаторФормы) <> Тип("УникальныйИдентификатор")
            Или ИдентификаторФормы <> ЭтаФорма.УникальныйИдентификатор
            Или ТипЗнч(ЗавершеннаяФаза) <> Тип("Число") Или ЗавершеннаяФаза <> Фаза85 Тогда
            ВызватьИсключение "Неизвестное, повторное или чужое завершение выгрузки";
        КонецЕсли;
        Если ТипЗнч(ПолученныеФайлы) <> Тип("Массив") Тогда
            ВызватьИсключение "Здесь нет диалога: неопределенный результат не объявляем отменой или успехом";
        КонецЕсли;
        Наблюдение = Новый Массив;
        Для Каждого ПереданныйФайл Из ПолученныеФайлы Цикл
            Если ТипЗнч(ПереданныйФайл) <> Тип("ОписаниеПереданногоФайла") Тогда
                ВызватьИсключение "Неизвестное описание фактически полученного файла";
            КонецЕсли;
            Если ТипЗнч(ПереданныйФайл.Имя) <> Тип("Строка") Или ТипЗнч(ПереданныйФайл.ПолноеИмя) <> Тип("Строка") Тогда
                ВызватьИсключение "Ожидаются фактические строковые имена без догадки о соответствии архиву";
            КонецЕсли;
            Наблюдение.Добавить(Новый ФиксированнаяСтруктура("Имя,ПолноеИмя", ПереданныйФайл.Имя, ПереданныйФайл.ПолноеИмя));
        КонецЦикла;
        ЖурналЗавершений85.Добавить(Новый ФиксированнаяСтруктура("Фаза,Описания", Фаза85, Новый ФиксированныйМассив(Наблюдение)));
        Если Фаза85 = 1 Тогда
            ПроверитьАрхив85();
        Иначе
            ПроверитьФайл85("C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Raw\invoice-b.json", Источники85[1]);
            ПроверитьФайл85("C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Raw\invoice-c.json", Источники85[2]);
            Если НайтиФайлы("C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Raw", "*", Ложь).Количество() <> 2 Тогда
                ВызватьИсключение "В тонком клиенте WhenRequired должен дать ровно два обычных JSON, без ZIP";
            КонецЕсли;
        КонецЕсли;
        ПроверитьПолныйИсточник85();
        Занят85 = Ложь;
        Если Фаза85 = 1 Тогда
            НачатьФазу85(2);
        Иначе
            Если ЖурналЗавершений85.Количество() <> 2 Тогда
                ВызватьИсключение "Нужны два настоящих завершения";
            КонецЕсли;
            Завершено85 = Истина;
            Состояние85 = "Проверено: ZIP A500, обычные B750/C1250; три источника 2500/100 байт не изменены";
        КонецЕсли;
    Исключение
        Остановить85();
        ВызватьИсключение;
    КонецПопытки;
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьФайл85(Путь, Источник)
    Если Путь <> "C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Extract\invoice-a.json"
        И Путь <> "C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Raw\invoice-b.json"
        И Путь <> "C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Raw\invoice-c.json" Тогда
        ВызватьИсключение "Нельзя читать произвольные файлы";
    КонецЕсли;
    ФайлРезультата = Новый Файл(Путь);
    Если Не ФайлРезультата.Существует() Тогда
        ВызватьИсключение "Нет заранее запланированного собственного результата";
    КонецЕсли;
    Если Не ФайлРезультата.ЭтоФайл() Тогда
        ВызватьИсключение "Ожидался собственный файл, а не каталог";
    КонецЕсли;
    ДанныеФайла = Новый ДвоичныеДанные(Путь);
    Если ДанныеФайла.Размер() <> Источник.Размер
        Или СтрЗаменить(СтрЗаменить(Base64Строка(ДанныеФайла), Символы.ВК, ""), Символы.ПС, "") <> Источник.Base64 Тогда
        ВызватьИсключение "Полные байты результата не совпали со своим исходным JSON";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьАрхив85()
    ПутьАрхива = "C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Always\always.zip";
    ФайлАрхива = Новый Файл(ПутьАрхива);
    Если Не ФайлАрхива.Существует() Тогда
        ВызватьИсключение "Собственный ZIP для единственного A500 отсутствует";
    КонецЕсли;
    Если Не ФайлАрхива.ЭтоФайл()
        Или НайтиФайлы("C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Always", "*", Ложь).Количество() <> 1 Тогда
        ВызватьИсключение "В своем Always должен находиться только созданный always.zip";
    КонецЕсли;
    ПроверитьПустойКаталог85("C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Extract");
    ЧтениеАрхива = Неопределено;
    ЗакрытиеНачато = Ложь;
    Попытка
        ЧтениеАрхива = Новый ЧтениеФайлаАрхива(ПутьАрхива);
        Если ТипЗнч(ЧтениеАрхива) <> Тип("ЧтениеФайлаАрхива") Тогда
            ВызватьИсключение "Не получен собственный актуальный reader";
        КонецЕсли;
        ЭлементыАрхива = ЧтениеАрхива.Элементы;
        Если ТипЗнч(ЭлементыАрхива) <> Тип("ЭлементыФайлаАрхива") Или ЭлементыАрхива.Количество() <> 1 Тогда
            ВызватьИсключение "Собственный архив должен содержать один исходный счет";
        КонецЕсли;
        Для Каждого ЭлементАрхива Из ЭлементыАрхива Цикл
            Если ТипЗнч(ЭлементАрхива) <> Тип("ЭлементФайлаАрхива")
                Или ЭлементАрхива.ИсходноеИмя <> "invoice-a.json"
                Или ЭлементАрхива.РазмерНесжатого <> 33 Или ЭлементАрхива.Зашифрован Тогда
                ВызватьИсключение "Архив не соответствует собственному A500: извлечение запрещено";
            КонецЕсли;
            ЧтениеАрхива.Извлечь(ЭлементАрхива, "C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Extract");
        КонецЦикла;
        ЗакрытиеНачато = Истина;
        ЧтениеАрхива.Закрыть();
    Исключение
        ИсходнаяОшибка = ОписаниеОшибки();
        ОшибкаЗакрытия = "";
        Если ЧтениеАрхива <> Неопределено И Не ЗакрытиеНачато Тогда
            ЗакрытиеНачато = Истина;
            Попытка
                ЧтениеАрхива.Закрыть();
            Исключение
                ОшибкаЗакрытия = ОписаниеОшибки();
            КонецПопытки;
        КонецЕсли;
        ВызватьИсключение "Ошибка проверки собственного архива: " + ИсходнаяОшибка + "; дополнительная ошибка закрытия: " + ОшибкаЗакрытия;
    КонецПопытки;
    ПроверитьФайл85("C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Extract\invoice-a.json", Источники85[0]);
    Если НайтиФайлы("C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\Output\Extract", "*", Ложь).Количество() <> 1 Тогда
        ВызватьИсключение "Не подтвержден единственный собственный извлеченный JSON";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура Остановить85()
    Остановка85 = Истина;
    Состояние85 = "СТОП: результат может быть частичным; не повторять, не удалять и не подтверждать успех";
КонецПроцедуры
Полный серверный модуль: собственные JSON и повторное чтение всех исходных байтов

Своя CF → Общие → Общие модули → УчебныйАрхивСервер85 → Модуль целиком

Сервер/Вызов сервераДа; оба клиентских флагаНет, без привилегий/повторного использования. Производит собственные literal payload и native временное хранилище своей формы; DB-записей нет.

Процедура ПроверитьСтенд85() Экспорт
    Если СтрокаСоединенияИнформационнойБазы() <> "File=""C:\1c-labs\OwnArchiveInvoiceExport5_661b6cb7-dd72-47ce-a19e-f0d347714047\IB"";" Или Метаданные.ОсновнойРежимЗапуска <> РежимЗапускаКлиентскогоПриложения.УправляемоеПриложение Или Метаданные.РежимСовместимости <> Метаданные.СвойстваОбъектов.РежимСовместимости.НеИспользовать Тогда
        ВызватьИсключение "Откройте только новую собственную файловую учебную базу 8.5.1.1343";
    КонецЕсли;
    Пользователь = ПользователиИнформационнойБазы.ТекущийПользователь();
    Если Пользователь = Неопределено Или ПользователиИнформационнойБазы.ПолучитьПользователей().Количество() <> 1 Тогда
        ВызватьИсключение "Нужен один собственный пользователь с административными правами учебной базы";
    КонецЕсли;
    Если Пользователь.Имя <> "УченикАрхива85" Тогда
        ВызватьИсключение "Это не пользователь собственного учебного стенда";
    КонецЕсли;
КонецПроцедуры

Функция План85() Экспорт
    ПроверитьСтенд85();
    План = Новый Массив;
    План.Добавить(Новый ФиксированнаяСтруктура("Имя,Количество,Цена,Сумма,Размер,Base64", "invoice-a.json", 2, 250, 500, 33, "eyJxdHkiOjIsInByaWNlIjoyNTAsInRvdGFsIjo1MDB9"));
    План.Добавить(Новый ФиксированнаяСтруктура("Имя,Количество,Цена,Сумма,Размер,Base64", "invoice-b.json", 3, 250, 750, 33, "eyJxdHkiOjMsInByaWNlIjoyNTAsInRvdGFsIjo3NTB9"));
    План.Добавить(Новый ФиксированнаяСтруктура("Имя,Количество,Цена,Сумма,Размер,Base64", "invoice-c.json", 5, 250, 1250, 34, "eyJxdHkiOjUsInByaWNlIjoyNTAsInRvdGFsIjoxMjUwfQ=="));
    Возврат Новый ФиксированныйМассив(План);
КонецФункции

Функция СоздатьИсточники85(ИдентификаторФормы) Экспорт
    ПроверитьСтенд85();
    Если ТипЗнч(ИдентификаторФормы) <> Тип("УникальныйИдентификатор") Тогда
        ВызватьИсключение "Нужен фактический UUID уже созданной собственной формы";
    КонецЕсли;
    Источники = Новый Массив;
    Для Каждого ПланСчета Из План85() Цикл
        Данные = Base64Значение(ПланСчета.Base64);
        Если ТипЗнч(Данные) <> Тип("ДвоичныеДанные") Или Данные.Размер() <> ПланСчета.Размер Тогда
            ВызватьИсключение "Не подтвержден собственный двоичный JSON";
        КонецЕсли;
        Адрес = ПоместитьВоВременноеХранилище(Данные, ИдентификаторФормы);
        Если ТипЗнч(Адрес) <> Тип("Строка") Или Адрес = "" Или Не ЭтоАдресВременногоХранилища(Адрес) Тогда
            ВызватьИсключение "Не получен реальный адрес временного хранилища";
        КонецЕсли;
        Источники.Добавить(Новый ФиксированнаяСтруктура("Имя,Количество,Цена,Сумма,Размер,Base64,Адрес", ПланСчета.Имя, ПланСчета.Количество, ПланСчета.Цена, ПланСчета.Сумма, ПланСчета.Размер, ПланСчета.Base64, Адрес));
    КонецЦикла;
    Результат = Новый ФиксированныйМассив(Источники);
    ПроверитьИсточники85(Результат);
    Возврат Результат;
КонецФункции

Процедура ПроверитьИсточники85(Источники) Экспорт
    ПроверитьСтенд85();
    Если ТипЗнч(Источники) <> Тип("ФиксированныйМассив") Или Источники.Количество() <> 3 Тогда
        ВызватьИсключение "Нужны все три собственных источника";
    КонецЕсли;
    План = План85();
    Номер = 0;
    Итого = 0;
    Байт = 0;
    Для Каждого Источник Из Источники Цикл
        Если ТипЗнч(Источник) <> Тип("ФиксированнаяСтруктура") Или Источник.Количество() <> 7 Тогда
            ВызватьИсключение "Нужны семь неизменяемых полей каждого исходного счета";
        КонецЕсли;
        Для Каждого ПолеПлана Из План[Номер] Цикл
            Если Не Источник.Свойство(ПолеПлана.Ключ, ЗначениеПоля)
                Или ТипЗнч(ЗначениеПоля) <> ТипЗнч(ПолеПлана.Значение)
                Или ЗначениеПоля <> ПолеПлана.Значение Тогда
                ВызватьИсключение "Изменен полный собственный источник";
            КонецЕсли;
        КонецЦикла;
        Если Не Источник.Свойство("Адрес", Адрес) Или ТипЗнч(Адрес) <> Тип("Строка")
            Или Адрес = "" Или Не ЭтоАдресВременногоХранилища(Адрес) Тогда
            ВызватьИсключение "Неверный адрес исходного JSON";
        КонецЕсли;
        Данные = ПолучитьИзВременногоХранилища(Адрес);
        Если ТипЗнч(Данные) <> Тип("ДвоичныеДанные") Или Данные.Размер() <> Источник.Размер
            Или СтрЗаменить(СтрЗаменить(Base64Строка(Данные), Символы.ВК, ""), Символы.ПС, "") <> Источник.Base64
            Или Источник.Сумма <> Источник.Количество * Источник.Цена Тогда
            ВызватьИсключение "Изменены байты, размер или независимый расчет исходного счета";
        КонецЕсли;
        Итого = Итого + Источник.Сумма;
        Байт = Байт + Данные.Размер();
        Номер = Номер + 1;
    КонецЦикла;
    Если Итого <> 2500 Или Байт <> 100 Или Номер <> 3 Тогда
        ВызватьИсключение "Все исходники должны оставаться 2500 и 100 байт";
    КонецЕсли;
КонецПроцедуры
Полный модуль приложения: открытие собственной формы без автоматической выгрузки

Своя CF → Модуль управляемого приложения целиком

Тонкий клиент Windows; настоящий запуск0 и собственный клиентский reference формы. Открытие не запускает файловый опыт.

Перем СвояФормаАрхива85, ГотовоПриложение85, ОстановкаПриложение85;

Процедура ПриНачалеРаботыСистемы()
    #Если Не ТонкийКлиент Тогда
        ВызватьИсключение "Учебный опыт разрешен только в тонком клиенте Windows";
    #КонецЕсли
    Если ГотовоПриложение85 <> Неопределено Или ОстановкаПриложение85 <> Неопределено Тогда
        ВызватьИсключение "Повторный запуск не сбрасывает состояние STOP";
    КонецЕсли;
    ГотовоПриложение85 = Ложь;
    ОстановкаПриложение85 = Ложь;
    Попытка
        УчебныйАрхивСервер85.ПроверитьСтенд85();
        Кандидат = ПолучитьФорму("ОбщаяФорма.Счета85", , , Истина);
        Если Кандидат = Неопределено Или ТипЗнч(Кандидат) <> Тип("ФормаКлиентскогоПриложения") Тогда
            ВызватьИсключение "Не создана собственная управляемая форма";
        КонецЕсли;
        Если Кандидат.ВладелецФормы <> Неопределено Или Кандидат.Открыта() Тогда
            ВызватьИсключение "Нужна новая самостоятельная форма";
        КонецЕсли;
        Кандидат.Открыть();
        Если Не Кандидат.Открыта() Тогда
            ВызватьИсключение "Открытие своей формы не подтверждено";
        КонецЕсли;
        Кандидат.ПроверитьГотовность85();
        СвояФормаАрхива85 = Кандидат;
        ГотовоПриложение85 = Истина;
    Исключение
        ОстановкаПриложение85 = Истина;
        ВызватьИсключение;
    КонецПопытки;
КонецПроцедуры

Функция ПолучитьСвоюФормуАрхива85() Экспорт
    Если ГотовоПриложение85 <> Истина Или ОстановкаПриложение85 <> Ложь
        Или ТипЗнч(СвояФормаАрхива85) <> Тип("ФормаКлиентскогоПриложения") Тогда
        ВызватьИсключение "Собственная форма не готова или приложение остановлено";
    КонецЕсли;
    Если Не СвояФормаАрхива85.Открыта() Или СвояФормаАрхива85.ВладелецФормы <> Неопределено Тогда
        ОстановкаПриложение85 = Истина;
        ВызватьИсключение "Своя форма закрыта или изменен владелец";
    КонецЕсли;
    Возврат СвояФормаАрхива85;
КонецФункции
Полный модуль своей общей команды: отдельное согласие начать учебную выгрузку

Своя CF → Общие → Общие команды → ВыгрузитьСчета85 → Модуль целиком

Тонкий клиент, настоящая ОбработкаКоманды2. Вызов экспортного старта своей формы; callback руками не вызывается.

&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
    Если ПараметрКоманды <> Неопределено
        Или ТипЗнч(ПараметрыВыполненияКоманды) <> Тип("ПараметрыВыполненияКоманды") Тогда
        ВызватьИсключение "Используйте только собственную команду без параметра";
    КонецЕсли;
    ФормаСчетов = ПолучитьСвоюФормуАрхива85();
    ФормаСчетов.НачатьВыгрузку85();
КонецПроцедуры

Разбор по шагам

  1. Подготовьте новую собственную файловую CF, тонкий клиент, форму, общую команду и три пустые папки.

  2. Настоящий запуск открывает readonly форму; только ваше отдельное выполнение команды начинает файловый опыт.

  3. Первый конструктор и Always упаковывают один JSON A500; реальный callback подтверждает результат, reader проверяет/извлекает его.

  4. Второй конструктор и WhenRequired в тонком клиенте возвращают B750/C1250 обычными файлами; число файлов не превращает режим в Always.

  5. Сравните полные байты собственных результатов и все семь полей каждого неизменяемого источника; одна ошибка оставляет STOP без повторов и удаления.

Как проверить результат

  1. Исходники: три ASCII UTF-8 JSON без BOM, 33+33+34=100 байт; суммы 500+750+1250=2500.

  2. Always\always.zip содержит только invoice-a.json; Extract\invoice-a.json совпадает полными байтами с A500.

  3. Raw содержит ровно invoice-b.json и invoice-c.json, совпадающие с B750/C1250, без when-required.zip.

  4. Все три исходных серверных payload и адреса неизменны до/после; GUI/компиляция/доставка файлов проверяются вами вручную только в своем стенде.

Ожидаемый результат

Один последовательный опыт показывает реальное различие режимов получения файлов и не принимает непроверенный частичный результат за успех.

На что обратить внимание

  1. ПолучатьАрхивПриНеобходимости архивирует только веб-клиент без расширения работы с файлами; в тонком клиенте здесь два обычных JSON.

  2. ИмяАрхива/Режим изменяемы, UUID формы/поля переданных описаний/содержимое архива только читаются. Нет ручного конструктора для callback результата.

  3. Контракт callback не доказывает соответствие описаний архивному файлу. Не проверяем догадку о количестве/именах массива; отдельно читаем собственные известные результаты.

  4. Find/Get коллекции архива имеют противоречивые 0-аргументные прототипы: они не используются и не засчитываются. Обход — настоящий foreach/Количество.

  5. Извлекается только созданный этим опытом собственный архив при отсутствии других изменяющих его процессов. Проверка одного ИсходноеИмя не делает урок защитой от произвольного чужого архива.

  6. Неизвестное завершение, отсутствие callback, исключение, изменение источника или закрытие ресурса не дают гарантии отката/атомарности/отсутствия частичных файлов. STOP остается, автоматического удаления нет.

  7. События запуска0, создания формы2, команды2 и завершения2 доставляет платформа. Самодельных вызовов этих событий, реальных файловых операций или runtime-доказательств здесь нет.

Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.

Не хотите разбираться сами?

Адаптируем это решение под вашу базу. Оценка бесплатно.

Оставить задачу

1С, 1С:Предприятие — товарные знаки ООО «1С». Сайт не является официальным представительством фирмы «1С» и не аффилирован с ней.