Каталог решений - Асинхронная запись и чтение файла без использования модальных методов и временных файлов

Асинхронная запись и чтение файла без использования модальных методов и временных файлов

Асинхронная запись и чтение файла без использования модальных методов и временных файлов

В наличии

В статье рассмотрено решение задачи формирования и записи файла на основе данных информационной базы. Формирование происходит на стороне сервера с помощью механизма потоков. Используются асинхронные методы передачи файлов между клиентом и сервером. Также рассмотрено решение обратной задачи — чтение файла и запись его данных в информационную базу. Статья ориентирована прежде всего на новичков, также приведенный код может использоваться в качестве шаблона.

Категория:

Описание

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

В те благословенные времена, когда о тонком клиенте и управляемых формах и не слышали В толстом клиенте на обычных формах эта задача решается довольно просто:

	Диалог = новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Сохранение);
	Если Диалог.Выбрать() Тогда   
		Запись = новый ЗаписьXML;
		Запись.ОткрытьФайл(Диалог.ПолноеИмяФайла);
		СериализаторXDTO.ЗаписатьXML(Запись, Ссылка.ПолучитьОбъект());
		Запись.Закрыть();	
	КонецЕсли; 

Здесь Ссылка — ссылка на элемент справочника, данные которого записываем в файл. СериализаторXDTO — специальный объект платформы, с помощью которого объект ИБ трансформируется в файл формата XML.

Все изменилось с появлением тонкого клиента и управляемых форм. Во-первых, на клиенте стала недоступна работа с объектами ИБ, а сервер, в свою очередь, ничего не знает о действиях пользователя, а во-вторых, использование веб-клиента породило необходимость отказаться от модальности и использовать асинхронные вызовы многих методов. Таким образом, решение приведенной выше задачи стало достаточно непростым.

Общая схема решения:

Рассмотрим каждый шаг более подробно.

1 шаг. Пользователь выбирает данные для файла (в данном случае — ссылку справочника) и хочет выбрать файл для записи. Использовать синхронные методы вроде Диалог.Выбрать() теперь стало нельзя. Вместо синхронных методов появились их асинхронные аналоги, в данном случае Диалог.Показать(Оповещение).

&НаКлиенте
Процедура Записать(Команда)
	Диалог = новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Сохранение);
	Оповещение = новый ОписаниеОповещения("ОповещениеПослеВыбораФайлаДляЗаписи", ЭтотОбъект, Ссылка);	 
	Диалог.Показать(Оповещение);	
КонецПроцедуры

Ключевой особенностью асинхронных методов является то, что результат их работы мы можем получить не сразу после вызова метода в том же участке кода, а с помощью отдельного метода (callback, метод обратного вызова), который будет вызван платформой после завершения асинхронного метода. В данном случае методом обратного вызова является ОповещениеПослеВыбораФайлаДляЗаписи, описание которого указывается в параметрах открытия диалога. Он будет вызван автоматически после закрытия диалога выбора файла вне зависимости от результата выбора.

2а шаг.

&НаКлиенте
Процедура ОповещениеПослеВыбораФайлаДляЗаписи(ВыбранныеФайлы, ДопПараметры)Экспорт
	Если ТипЗнч(ВыбранныеФайлы) = Тип("Массив")	Тогда
		АдресХранилища = ФормированиеФайлаНаСервере(ДопПараметры, ЭтаФорма.УникальныйИдентификатор);		
		Оповещение = новый ОписаниеОповещения("ОповещениеПослеПолученияФайла", ЭтотОбъект);		
		ОписаниеФайла = новый ОписаниеПередаваемогоФайла();
		ОписаниеФайла.Хранение = АдресХранилища;
		ОписаниеФайла.Имя = ВыбранныеФайлы[0];
		ПолучаемыеФайлы = Новый Массив;
		ПолучаемыеФайлы.Добавить(ОписаниеФайла);		
		НачатьПолучениеФайлов(Оповещение, ПолучаемыеФайлы,, Ложь);
	КонецЕсли;
КонецПроцедуры 

После закрытия диалога выбора файла, и проверки, что пользователь действительно не отказался от выбора (в первом параметре метода будет находится массив выбранных файлов), мы можем продолжить процесс. И здесь программиста подстерегает возможность совершить трудноуловимую ошибку. Дело в том, что в веб-клиенте во время показа пользователю диалога выбора файла выполнение кода не останавливается! Существует вероятность, что за время между первым и вторым шагом изменятся данные, которые пользователь выбрал на первом шаге (в нашем примере — Ссылка). Чтобы этого избежать, данные надо где-то сохранить, а еще лучше — передать в качестве параметра при открытии диалога, так, чтобы этот параметр попал на второй шаг без изменений. В нашем примере это реализовано указанием ссылки в качестве третьего параметра при создании оповещения на первом шаге. Эта же ссылка будет получена вторым параметром метода ОповещениеПослеВыбораФайлаДляЗаписи. Итак мы имеем все необходимые данные (имя файла и ссылку) для записи. Напомню: пока все то, что происходит, происходит на стороне клиента. Но на стороне клиента недоступны данные ИБ, поэтому нам необходимо передать управление на сервер, вызвав серверный метод и передав в него ссылку.

3 шаг

На этом шаге нам необходимо сформировать содержимое файла и подготовить его для передачи на сторону клиента, поместив в специальную область — временное хранилище. Временное хранилище располагается на сервере, доступ к нему осуществляется с помощью специального адреса. Временное хранилище "живет" непродолжительное время, в нашем случае — пока живет форма, для этого при создании хранилища указывается идентификатор формы. В хранилище будем помещать ДвоичныеДанные — содержимое нашего будущего файла. Как правило, использование двоичных данных связано с формированием промежуточного временного файла, но в нашем случае мы можем обойтись без этого. В последних версиях платформы появились новые объекты — Поток, ПотокВПамяти, которые позволят нам обойтись без дополнительных файлов.

&НаСервереБезКонтекста 
Функция ФормированиеФайлаНаСервере(Ссылка, ИдентификаторФормы)	
	Запись = новый ЗаписьXML;
	Поток = новый ПотокВПамяти;
	Запись.ОткрытьПоток(Поток);
	СериализаторXDTO.ЗаписатьXML(Запись, Ссылка.ПолучитьОбъект());
	Запись.Закрыть();	
	ДвоичныеДанные = Поток.ЗакрытьИПолучитьДвоичныеДанные();
	АдресХранилища = ПоместитьВоВременноеХранилище(ДвоичныеДанные, ИдентификаторФормы);	
	Возврат АдресХранилища;
КонецФункции 

В конечном итоге на этом шаге мы получим адрес хранилища, в котором находится содержимое нашего файла.

2б шаг

Вызов серверной функции на втором шаге был обычным, не асинхронным, поэтому после выполнения этого метода мы продолжаем выполнение второго шага. Итак, имеем: имя выбранного пользователем файла и адрес временного хранилища на сервере, где хранится его содержимое. Необходимо вызвать метод, который получит содержимое с сервера и запишет в файл на клиенте. Этот метод — НачатьПолучениеФайлов. Не буду останавливаться на подробном описании его параметров, скажу лишь, что в него передается имя файла и адрес временного хранилища. Метод НачатьПолучениеФайлов является асинхронным, окончание его работы запустит метод обратного вызова ОповещениеПослеПолученияФайла, в котором мы узнаем результат. Разумеется, описание метода ОповещениеПослеПолученияФайла мы должны указать при вызове НачатьПолучениеФайлов.

4 шаг

Этот шаг — завершающий. На этом шаге мы можем проанализировать результат получения файла.

&НаКлиенте
Процедура ОповещениеПослеПолученияФайла(ПолученныеФайлы, ДопПараметры)Экспорт
	Если ТипЗнч(ПолученныеФайлы) = Тип("Массив")	Тогда
		Сообщить("Файл " + ПолученныеФайлы[0].Имя + " записан"); 
	КонецЕсли; 	
КонецПроцедуры // 

 

Аналогичным образом можно решить обратную задачу: загрузить данные из файла в информационную базу

1 шаг. Открываем асинхронный диалог выбора файла, передав в него все необходимые клиентские данные (в нашем случае — Ссылка)

&НаКлиенте
Процедура Прочитать(Команда)
	Диалог = новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
	Оповещение = новый ОписаниеОповещения("ОповещениеПослеВыбораФайлаДляЧтения", ЭтотОбъект, Ссылка);	 
	Диалог.Показать(Оповещение);	
КонецПроцедуры

2 шаг. Если пользователь выбрал файл, то инициируем асинхронную передачу файла на сервер с помощью НачатьПомещениеФайлов. Платформа автоматически создаст на сервере временное хранилище и поместит в него содержимое файла.

&НаКлиенте
Процедура ОповещениеПослеВыбораФайлаДляЧтения(ВыбранныеФайлы, ДопПараметры)Экспорт
	Если ТипЗнч(ВыбранныеФайлы) = Тип("Массив")	Тогда
		ОписаниеФайла = новый ОписаниеПередаваемогоФайла();
		ОписаниеФайла.Имя = ВыбранныеФайлы[0];
		ПомещаемыеФайлы = новый Массив;
		ПомещаемыеФайлы.Добавить(ОписаниеФайла);
		Оповещение = новый ОписаниеОповещения("ОповещениеПомещениеФайла", ЭтотОбъект, ДопПараметры);		
		НачатьПомещениеФайлов(Оповещение, ПомещаемыеФайлы,, Ложь, ЭтаФорма.УникальныйИдентификатор);
	КонецЕсли;
КонецПроцедуры 

3а шаг. После завершения операции помещения файла платформа вызовет клиентскую процедуру, в которую будут переданы данные о помещенных на сервер файлах. Нас будет интересовать адрес хранилища ПомещенныеФайлы[0].Хранение, в которое записано содержимое файла. Для обработки этого содержимого вызовем серверную функцию, передав в нее этот адрес и данные, выбранные клиентом на первом шаге. Еще раз подчеркну, что эти клиентские данные (в нашем случае — Ссылка) передаются по цепочке оповещений, что гарантирует их неизменность во время асинхронного выполнения нескольких методов.

&НаКлиенте
Процедура ОповещениеПомещениеФайла(ПомещенныеФайлы, ДопПараметры)Экспорт
	Если ТипЗнч(ПомещенныеФайлы) = Тип("Массив")	Тогда
		Сообщить("Файл " + ПомещенныеФайлы[0].Имя + " отправлен на сервер"); 
		Ссылка = ОбработкаПереданногоФайлаНаСервере(ПомещенныеФайлы[0].Хранение, ДопПараметры);
		ЭтаФорма.ОтобразитьИзменениеДанных(Ссылка,ВидИзмененияДанных.Изменение);
	КонецЕсли; 	
КонецПроцедуры  

4 шаг. Получив на сервере адрес временного хранилища, прочитаем его содержимое в ДвоичныеДанные. Так же, как и в предыдущем примере, чтобы не создавать дополнительных временных файлов, воспользуемся потоками. С помощью СериализаторXDTO преобразуем данные (они представляют собой XML текст) в объект для дальнейшей обработки. 

&НаСервереБезКонтекста 
Функция ОбработкаПереданногоФайлаНаСервере(Знач АдресХранилища, Знач Ссылка)
	ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресХранилища);	
	Поток = ДвоичныеДанные.ОткрытьПотокДляЧтения();	
	Чтение = новый ЧтениеXML;
	Чтение.ОткрытьПоток(Поток);
	Объект = СериализаторXDTO.ПрочитатьXML(Чтение);
	Если ЗначениеЗаполнено(Ссылка)  Тогда   
		ОбъектСсылки = Ссылка.ПолучитьОбъект();
		ЗаполнитьЗначенияСвойств(ОбъектСсылки, Объект, "Наименование");  //Заполняем нужные реквизиты
		ОбъектСсылки.Записать();  		
	Иначе
		Объект.Записать();	
		Ссылка = Объект.Ссылка;
	КонецЕсли; 	
	Чтение.Закрыть();	
	Поток.Закрыть();	
	Возврат Ссылка;
КонецФункции 

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

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

Работа с файлами в тонком клиенте не ограничивается вышеприведенными примерами. Для загрузки файлов (например картинок) непосредственно в информационную базу рекомендую публикации //sale.itcity.ru/public/195003/, //sale.itcity.ru/public/396459/

Ниже прилагается внешняя обработка с примерами из статьи.

 

has been added to your cart:
Оформление заказа