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

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

Перейти к публикации

Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. palsergeich 10.04.18 11:37 Сейчас в теме
Есть один маленький и очень тонкий ньюанс:
При использовании временного хранилища с адресом УИД формы, если надо передать больше чем 1 файл, точнее передать потом еще раз передать, то в дело вступает механизм кеширования и результат будет следующий (не знаю починили ли в последних релизах):
Поместили Адрес = ПоместитьВоВременноеХранилище(Файл1,ЭтаФорма.УникальныйИдентификатор)
Метод ПолучитьИзВременногоХранилища(Адрес)
На сервере - Файл 1
На клиенте - Файл 1
следом Адрес = ПоместитьВоВременноеХранилище(Файл2,ЭтаФорма.УникальныйИдентификатор) Форма та же, без переоткрытия
Метод ПолучитьИзВременногоХранилища(Адрес)
На сервере Файл 2
На клиенте Файл 1
При этом где помещаем - клиент или сервер - не важно.
2. Alxby 516 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 216 11.04.18 03:56 Сейчас в теме
(3)
изменение по одному адресу во временном хранилище
и как выкрутились с адресом во временном хранилище?
4. LexSeIch 207 11.04.18 03:25 Сейчас в теме
Интересная и полезная статья, особенно для тех, кто под "нажимом непреодолимой силы" вынужден менять привычные платформы и конфигурации... При переписывании обработок на управляемые формы вопрос корректной работы с файлами обязательно возникает...
6. logos 184 11.04.18 10:51 Сейчас в теме
Давайте я немного наброшу на вентилятор. А как будет работать Ваша схема с файлами больше 4 Гб?
7. Alxby 516 11.04.18 11:45 Сейчас в теме
(6)
Я бы слегка перефразировал вопрос и разбил его на несколько:
1) Как работают с большими файлами платформенные механизмы передачи файлов между клиентом и сервером? Какие есть ограничения для тонкого и веб-клиента? Есть ли зависимость от браузера?
2) Как работают с большими объемами данных механизмы потоков?
Впрочем, мне кажется, что ответы на эти вопросы имеют скорее теоретическое значение, так как если система спроектирована таким образом, что передача больших файлов указанными средствами платформы - штатная функция системы, то это недосмотр проектировщика или архитектора. Все же подобные задачи лучше решать другими средствами, к примеру - FTP. А для "защиты от дурака" достаточно встроить проверку размера файла перед передачей. Да, конечно же не забываем, что на клиенте для этого необходимо использовать асинхронный НачатьПолучениеРазмера
8. logos 184 11.04.18 13:56 Сейчас в теме
(7)
1) Какая разница как они работают "под капотом"? Во временное хранилище нельзя положить больше 2^32 байт. Просто нельзя.
Если Вам сильно интересно, как, возьмите большой файл и посмотрите на дисковую активность в темпах, обещаю понравится.
2) А вот механизм потоков работает просто отлично, он может и небольшими порциями работать через ПотокВПамяти.
9. Alxby 516 11.04.18 14:20 Сейчас в теме
(8)
Ну вот вы и сами ответили на свой вопрос, попутно подтвердив мои слова, о том, что при проектировании системы, работающей с большими файлами, нельзя использовать механизмы передачи файлов через временное хранилище. Для этого необходимо использовать другие технологии.
10. androgin 12.04.18 01:01 Сейчас в теме
Я конечно не хочу казаться умником, но чем вас не устраивает метод НачатьПомещениеФайла / НачатьПомещениеФайлов ? У вас куча процедур расписана (на мой взгляд совершенно лишних)

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

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

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

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

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