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

Программирование - Практика программирования

Файлы; работа с файлами; запись файла; передача файлов; потоки;

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

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

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

	Диалог = новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Сохранение);
	Если Диалог.Выбрать() Тогда   
		Запись = новый Запись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б шаг. После завершения серверной функции, управление передается обратно в клиентский метод (вызов был не асинхронный), где мы можем выполнить все оставшиеся действия (в нашем случае - обновить интерфейс после загрузки данных)

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

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

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

 

114

Скачать файлы

Наименование Файл Версия Размер
Примеры работы с файлами
.epf 9,75Kb
10.04.18
13
.epf 9,75Kb 13 Скачать

См. также

Комментарии
Избранное Подписка Сортировка: Древо
1. palsergeich 10.04.18 11:37 Сейчас в теме
Есть один маленький и очень тонкий ньюанс:
При использовании временного хранилища с адресом УИД формы, если надо передать больше чем 1 файл, точнее передать потом еще раз передать, то в дело вступает механизм кеширования и результат будет следующий (не знаю починили ли в последних релизах):
Поместили Адрес = ПоместитьВоВременноеХранилище(Файл1,ЭтаФорма.УникальныйИдентификатор)
Метод ПолучитьИзВременногоХранилища(Адрес)
На сервере - Файл 1
На клиенте - Файл 1
следом Адрес = ПоместитьВоВременноеХранилище(Файл2,ЭтаФорма.УникальныйИдентификатор) Форма та же, без переоткрытия
Метод ПолучитьИзВременногоХранилища(Адрес)
На сервере Файл 2
На клиенте Файл 1
При этом где помещаем - клиент или сервер - не важно.
2. Alxby 372 10.04.18 13:38 Сейчас в теме
(1) На платформе 8.3.10.2699 провел несколько экспериментов:
&НаКлиенте   
Процедура Команда1(Команда)
	//1 эксперимент
	Адрес = ПоместитьВоВременноеХранилище("А", ЭтаФорма.УникальныйИдентификатор);
	Сообщить(ПолучитьИзВременногоХранилища(Адрес));
	ПоместитьВоВременноеХранилище("Б", Адрес);
	Сообщить(ПолучитьИзВременногоХранилища(Адрес));

	//2 эксперимент
	Адрес1 = ПоместитьВоВременноеХранилище("А", ЭтаФорма.УникальныйИдентификатор);
	Сообщить(ПолучитьИзВременногоХранилища(Адрес1));
	Адрес2 = ПоместитьВоВременноеХранилище("Б", ЭтаФорма.УникальныйИдентификатор);
	Сообщить(ПолучитьИзВременногоХранилища(Адрес2));
	
	//3 эксперимент
	Адрес = ПоместитьВоВременноеХранилище("А", ЭтаФорма.УникальныйИдентификатор);
	ПолучитьНаСервере(Адрес);
	ПоместитьВоВременноеХранилище("Б", Адрес);
	ПолучитьНаСервере(Адрес);

	//4 эксперимент
	Адрес1 = ПоместитьВоВременноеХранилище("А", ЭтаФорма.УникальныйИдентификатор);
	ПолучитьНаСервере(Адрес1);
	Адрес2 = ПоместитьВоВременноеХранилище("Б", ЭтаФорма.УникальныйИдентификатор);
	ПолучитьНаСервере(Адрес2);
	
КонецПроцедуры

&НаСервереБезКонтекста  
Процедура ПолучитьНаСервере(Адрес)
	Сообщить(ПолучитьИзВременногоХранилища(Адрес));
КонецПроцедуры
Показать


Результат получился вполне ожидаемый, т.е. правильный. Возможно в предыдущих релизах платформы действительно была ошибка, но сейчас она исправлена.
3. palsergeich 10.04.18 16:01 Сейчас в теме
(2)
УникальныйИдентификатор);
ПолучитьНаСервере(Адрес1);
Адрес2 = ПоместитьВоВременноеХранилище("Б", ЭтаФорма.УникальныйИдентификатор);
ПолучитьНаСервере(Адрес2);

Там суть моего посыла была в другом, но по факту да, проверил на текущих релизах, при получении из временного хранилища по адресу формы больше бага нет.
Но я уже привык не доверять этому механизму, ибо в свое время на этом очень сильно обжегся (изменение по одному адресу во временном хранилище)
5. rusmil 123 11.04.18 03:56 Сейчас в теме
(3)
изменение по одному адресу во временном хранилище
и как выкрутились с адресом во временном хранилище?
4. LexSeIch 194 11.04.18 03:25 Сейчас в теме
Интересная и полезная статья, особенно для тех, кто под "нажимом непреодолимой силы" вынужден менять привычные платформы и конфигурации... При переписывании обработок на управляемые формы вопрос корректной работы с файлами обязательно возникает...
6. logos 72 11.04.18 10:51 Сейчас в теме
Давайте я немного наброшу на вентилятор. А как будет работать Ваша схема с файлами больше 4 Гб?
7. Alxby 372 11.04.18 11:45 Сейчас в теме
(6)
Я бы слегка перефразировал вопрос и разбил его на несколько:
1) Как работают с большими файлами платформенные механизмы передачи файлов между клиентом и сервером? Какие есть ограничения для тонкого и веб-клиента? Есть ли зависимость от браузера?
2) Как работают с большими объемами данных механизмы потоков?
Впрочем, мне кажется, что ответы на эти вопросы имеют скорее теоретическое значение, так как если система спроектирована таким образом, что передача больших файлов указанными средствами платформы - штатная функция системы, то это недосмотр проектировщика или архитектора. Все же подобные задачи лучше решать другими средствами, к примеру - FTP. А для "защиты от дурака" достаточно встроить проверку размера файла перед передачей. Да, конечно же не забываем, что на клиенте для этого необходимо использовать асинхронный НачатьПолучениеРазмера
8. logos 72 11.04.18 13:56 Сейчас в теме
(7)
1) Какая разница как они работают "под капотом"? Во временное хранилище нельзя положить больше 2^32 байт. Просто нельзя.
Если Вам сильно интересно, как, возьмите большой файл и посмотрите на дисковую активность в темпах, обещаю понравится.
2) А вот механизм потоков работает просто отлично, он может и небольшими порциями работать через ПотокВПамяти.
9. Alxby 372 11.04.18 14:20 Сейчас в теме
(8)
Ну вот вы и сами ответили на свой вопрос, попутно подтвердив мои слова, о том, что при проектировании системы, работающей с большими файлами, нельзя использовать механизмы передачи файлов через временное хранилище. Для этого необходимо использовать другие технологии.
10. androgin 12.04.18 01:01 Сейчас в теме
Я конечно не хочу казаться умником, но чем вас не устраивает метод НачатьПомещениеФайла / НачатьПомещениеФайлов ? У вас куча процедур расписана (на мой взгляд совершенно лишних)

я выбираю файлы таким образом:

&НаКлиенте
Процедура ВыбратьФайл(Команда)
    
    Диалог = Новый ДиалогВыбораФайла(РежимДиалогавыборафайла.Открытие);
    Диалог.Заголовок = "Выберите файл...";
    Диалог.Фильтр = "Текстовый документ MS Office (*.doc;*.docx)|*.doc;*.docx";
    
    Оповещение = Новый ОписаниеОповещения("ЗакончитьПомещениеФайла", ЭтотОбъект);
    НачатьПомещениеФайлов(Оповещение, , Диалог, Истина, ЭтаФорма.УникальныйИдентификатор);
    
КонецПроцедуры 

&НаКлиенте
Процедура ЗакончитьПомещениеФайла(ПомещенныеФайлы, ДопПараметры) Экспорт

    Если ПомещенныеФайлы = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    Хранение = ПомещенныеФайлы[0].Хранение;

КонецПроцедуры
Показать
11. Alxby 372 12.04.18 09:35 Сейчас в теме
(10) Основное отличие моего варианта от Вашего - возможность обработать выбранный пользователем файл до передачи на сервер или получения с сервера. Например: сообщить пользователю "Вы не можете загрузить на сервер файл 'Игра престолов (все сезоны).mkv'" или "Вы не можете перезаписать файл 'устав проекта.docx', выберите другой". Кроме того, в Вашем варианте все заканчивается после получения адреса загруженного файла, в моем - дополнительно описана процедура обработки содержимого с помощью механизма потоков, поэтому и методов в примере больше. Но конечно же, Ваш вариант проще, и может быть вполне применим в большинстве случаев.
12. androgin 12.04.18 13:48 Сейчас в теме
(11) при превышении объема файла над размером памяти - система сама выдаст ошибку. Вам останется лишь вывести ее в удобочитаемом виде.
14. Alxby 372 12.04.18 15:04 Сейчас в теме
(12)

12. Виктор Назаров (androgin) 12.04.18 13:48
(11) при превышении объема файла над размером памяти - система сама выдаст ошибку. Вам останется лишь вывести ее в удобочитаемом виде

А если нам надо ограничить размер файла своим значением, например 10Мб, исходя из своих целей? А если надо наложить условие: можно загружать все файлы, кроме определенных типов? В этих случаях как раз и надо проверить, что выбрал пользователь. Причем сделать это лучше ДО остальных действий по формированию и передаче файла.
13. androgin 12.04.18 13:52 Сейчас в теме
(11) еще можно создать диалог - выбрать и инициализировать файл и получить его размер. Если размер устраивает - передать Диалог дальше в метод получения файла.
15. Alxby 372 12.04.18 15:12 Сейчас в теме
(13) Именно такая схема, с отдельным диалогом, и приведена в статье. Я не стал вставлять дополнительные проверки, чтобы не загромождать пример. И, раз уж мы получили имя файла, его и надо передавать в метод передачи файла, а не Диалог.
16. androgin 13.04.18 20:07 Сейчас в теме
(15) ну вам виднее. Но я бы так не делал)
17. androgin 13.04.18 20:16 Сейчас в теме
(15) вы в своем коде все равно тащите все на сервер - в чем смысл?
вам в любом случае для получения размера сначала придется инициализировать файл, а до этого выбрать его диалогом. Все не удовлетворяющие файлы можно удалить из диалога и передать уже этот диалог в получение файлов.
Использование диалога позволяет применять фильтр!
НЕ?
18. Alxby 372 13.04.18 21:04 Сейчас в теме
(17)
Мне кажется в нашем споре каждый имеет в виду что-то свое. Я описываю решение следующей задачи: 1) дать возможность пользователю выбрать файл, 2) проверить, что он выбрал (проверки могут быть самые разные: размер, расширение, каталог), 3) и только если файл удовлетворяет заданным критериям, отправить его на сервер для дальнейшей обработки. В моем примере выбор файла - асинхронный вызов диалога, по его завершению мы получим имя файла, который после всех проверок будем использовать при передачи на сервер. Зачем при передаче на сервер еще раз указывать диалог? Чтобы пользователь два раза выбирал файл? Да, конечно же, можно использовать интерактивный режим вызова метода НачатьПомещениеФайлов без предварительного вызова диалога. Но при этом Вам просто негде будет вставить свои проверки. Единственное что Вы можете - это указать маску допустимых имен файла. Но и в этом случае Вы не сможете задать условие вида: 'выбрать все файлы, кроме *.exe, *.vbs'. Что касается задачи получения файла с сервера, там ситуация немного иная. Если, как в моем примере, файл не хранится в базе, а формируется каким либо образом - это формирование может занять какое-то время. Тогда, с точки зрения юзабилити, лучше дать возможность выбрать файл для записи, удостовериться что он его выбрал, и выбрал то, что надо, а только потом заниматься длительным формированием файла. При использовании Вашего подхода (т.е. использования диалога выбора при вызове метода передачи) мы сначала затратим время на формирование и сохранение файла во временном хранилище, а только потом будем спрашивать у пользователя куда он хочет его сохранить, и не передумал ли он, "потому что забыл дома флешку".
19. androgin 13.04.18 21:30 Сейчас в теме
(18) так вы сами же описываете то, что я и говорю: выбор файла и проверку его ( ваш второй пункт очень странный - расширение и каталог в диалоге выбирается! а размер можно получить после выбора файла или настроить вид диалога - показать там размер).
Это топтание на месте.
Мне вообще непонятно: зачем использовать сервер, если все на клиенте делается: и выбор файла и получение его размера. Только для того, чтобы поиграться с потоком?)))
Можно использовать и не_интерактивный метод выбора (признак такой имеется в методе) и это никак не мешает вставить свои проверки. Фильтр "выбрать все, кроме ххх, ххх" - ну это ж дурость! Создайте необходимые фильтры заранее и пусть пользователь сам выбирает, что ему нужно.
Когда вы тащите хранилище на сервер - вы уже получаете файл!
Вам вообще знакомы клиент-серверные работы с файлами?
Ваш метод, мало того, что получает файл и помещает в хранилище, так вы потом его еще на сервер тащите и там получаете из хранилища!!
Что за абсурд?
Все ваши манипуляции вполне себе на клиенте работают!
20. Alxby 372 13.04.18 22:19 Сейчас в теме
(19)
Несмотря на вашу горячность, я надеюсь, что Вы не троллите, а просто невнимательно читаете мои сообщения. Хорошо, давайте возьмем Ваш пример из (10). Добавьте в него возможность загружать любые файлы, кроме потенциально опасных, например *.exe. Нет, это не дурость, подобный функционал есть в системах, построенных на базе БСП, там есть что-то вроде "Запретить загрузку файлов с расширениями". В случае попытки выбрать такой файл добавьте сообщение об этом пользователю. Если у Вас найдется способ сделать это с помощью интерактивного режима метода НачатьПомещениеФайлов без дополнительного вызова диалога, поделитесь. Далее: в моем примере при выборе файла не используется сервер, и конечно же для получения размера файла сервер тоже не нужен. Безусловно, есть задачи, которые можно решить только на клиенте. Но я специально выбрал такую задачу, для которой нужен сервер. Я это подчеркнул в начале статьи. Вы и сами сможете привести примеры таких задач. Вы же согласитесь, что с объектами базы данных можно работать только на сервере? Вот и в моем примере используется запись и чтение объектов ИБ. Может Вы имеете в виду иное - хранение файла целиком в базе данных, в каком-либо реквизите? Это немного другая задача.
22. androgin 16.04.18 00:30 Сейчас в теме
(20) "кроме потенциально опасных" - вы вообще читали, что я писал?
вы программист - вам легко это проверить после выбора! Даже если вы не укажете опасное расширение - вы его можете проверить ПОСЛЕ выбора и вывести пользователю сообщение, что "ай-я-яй, такие файлы нельзя выбирать!" и удалить их из выбора. Сложно? НЕТ!
Далее: НачатьПомещениеФайлов с диалогом как раз и контролирует расширения, о чем я вам выше и писал!
"специально выбрал такую задачу, для которой нужен сервер" - именно это и является узким местом! вы тащите все на сервер! а это и есть "очень плохо".
Я же настаиваю, что все операции с проверками делать нужно на клиенте!
23. Alxby 372 16.04.18 10:04 Сейчас в теме
(22)
Вы все-таки невнимательно прочитали статью и мои комментарии. Поясню еще раз: на первом шаге мы вызываем диалог выбора файла. При вызове можно указать (как Вы верно говорите) свойства диалога: фильтр, начальный каталог, etc. Результат работы диалога обрабатывается в ОповещениеПослеВыбораФайлаДляЧтения . Здесь мы можем выполнить любые проверки - проверить расширение файла, путь к файлу и так далее. Я не стал загромождать пример, поэтому проверяю только то, что пользователь не отказался от выбора. Если вы посмотрите внимательно, то увидите директиву &НаКлиенте. Это означает, что эти операции будут выполнены на клиенте. С чего Вы взяли, что я делаю это на сервере? Если посмотрите внимательно, то увидите, что у меня ни в одной серверной процедуре нет проверок.
Вы разобрались что происходит в моих серверных методах? А там я делаю то, что можно сделать только на сервере: работаю с данными информационной базы. Для решения поставленной в начале статьи задачи, мне нужно содержимое файла. Вполне логично, что я его получаю из хранилища. Что значат Ваши слова:
"Ваш метод, мало того, что получает файл и помещает в хранилище, так вы потом его еще на сервер тащите и там получаете из хранилища!!
Что за абсурд?
Все ваши манипуляции вполне себе на клиенте работают! "? Как можно после помещения файла во временное хранилище, отдельно его "тащить" на сервер? Какая строчка моего кода заставила Вас так подумать? Как "манипуляции" по созданию объекта и записи его в ИБ "вполне себе на клиенте работают"? Или Вы считаете что таких задач не бывает? Что любые действия с содержимым файлов можно делать на клиенте, потому что на сервере это делать "есть "очень плохо"?
Давайте, чтобы не быть голословным, напишите свой вариант решения рассмотренной в статье задачи и покажите в чем он быстрее / лучше / пригодней для дальнейшей адаптации. Потому что иначе от меня ускользает смысл того, что Вы хотите доказать.
Оставьте свое сообщение