Как передать документ Word (ActiveDocument или ДвоичныеДанные) с сервера на клиент
Когда мы работаем в файловом варианте и заполняем Word программно, можно не заморачиваться с передачей Word-овского документа на клиент, т.к. после методов <MSWord.Application.Visible = Истина; и MSWord.Activate();> выполненных НаСервере Word-овский документ отобразится на экране монитора пользователя, но в клиент-серверном варианте так просто поступить не получится.
(1) Franco, в "Альтернативы:" я уже дал ссылку на эту статью
(2) w.r., А в чём вопрос то? Боитесь, что значение будет удалено раньше чем вы успеете его использовать?
Кстати для 2-го примера <Передача макет Word (Двоичные данные) с сервера на клиент> можно и не писать
,т.к. в СтруктураВсехДокументов попадает СтруктураПараметров, в которую в свою очередь попадает только строковые значения (какое значение на какое заменить). Для строковых значений не нужно сериализовать значение. Именно эта строка написана для примера. Если вы так-же как и я в СтруктураПараметров будете писать только строковые значения, то можно и не помещать во временное хранилище.
У меня даже нет переменной где бы я запомнил адрес во временном хранилище, т.к. как только я делаю "ПоместитьВоВременноеХранилище" потом сразу делаю "ПолучитьИзВременногоХранилища". Между этими двумя действиями нет ни строчки кода, поэтому пропасть значение до получения не может. Кстати код у меня установлен в рабочей базе и всё работает.
Если же кому-то исходя из специфики задачи нужен будет этот адрес не единожды и он его будет потом использовать вплоть до закрытия формы, то конечно корректней использовать УникальныйИдентификатор.
Другими словами: замечание хорошее, но в моей конкретной реализации задачи ничего не потеряется даже без уникального идентификатора.
Спасибо за замечание, статью позже чуть допишу (вдруг кому-то и правда адрес во временном хранилище нужен будет не единожды и сразу).
У меня даже нет переменной где бы я запомнил адрес во временном хранилище, т.к. как только я делаю "ПоместитьВоВременноеХранилище" потом сразу делаю "ПолучитьИзВременногоХранилища". Между этими двумя действиями нет ни строчки кода, поэтому пропасть значение до получения не может.
Вы не внимательно читали видимо описание. Написано:
значение будет удалено после очередного запроса сервера из общего модуля
Т.е. любое выполнение регламентного задания или иных действий параллельно работающих пользователей, использующих общие модули, в промежутке выполнения вашего кода на форме очистит временное хранилище, куда вы поместили макет еше до получения из хранилища
(7) w.r., Провожу следующий эксперимент:
Запускаю отладку.
На коде "Возврат Документы.ПисьмоНаОплату.ПечатьПисьма(МассивОбъектов, ОбъектыПечати, Word);" нажимаю F11. Захожу в модуль менеджера, через "Возврат ПоместитьВоВременноеХранилище(СтруктураВсехДокументов);) успешно возвращаюсь обратно на форму в функцию:
&НаСервере
Функция ПечатьПисьма(МассивОбъектов, ОбъектыПечати, Word)
Возврат Документы.ПисьмоНаОплату.ПечатьПисьма(МассивОбъектов, ОбъектыПечати, Word);
КонецФункции
Я НаСервере в форме в процедуре ПечатьПисьма (см. скрин). Во временное хранилище произошло помещение объекта, но на клиент ещё не передано (и соответственно не получено из временного хранилища).
Жду 10 минут. Рабочая база, около 100 активных пользователей. За эти 10 минут было выполнено как минимум 4 фоновых задания (часть из них ни один раз) (см. скрин). Я открыл 2 сеанс, там вывел на печать письмо в Word. Ну явно за 10 минут одно из выше перечисленных условий для удаления файла было выполнено (если вы эти условия читаете в контексте всех пользователей и всех сеансов).
Далее 10 минут спустя я жму F11 выхожу на клиент и у меня он спокойно получает из временного хранилища и выходит файл Word на экран моего монитора.
При одновременной работе с файлом на клиенте и на сервере необходимо использовать передачу файла через временное хранилище (методы ПоместитьФайл, ПоместитьФайлы, ПолучитьФайл, ПолучитьФайлы, НачатьПомещениеФайла, ПоместитьВоВременноеХранилище, ПолучитьИзВременногоХранилища)...
....
Для сохранения данных во временном хранилище между несколькими серверными вызовами, при помещении его в хранилище необходимо использовать параметр УникальныйИдентификаторФормы метода ПоместитьФайл, передав в него идентификатор текущей формы. Такие значение будут удалены их временного хранилища только при закрытии указанной формы.
Там конечно говорится про другой метод, но всё-таки имеется ввиду серверный вызов в пределах одного сеанса / одного кода, а не всех пользователей! Для указанного там в примере метода "Переместитьфайл" то-же самое написано в СП как и для ПоместитьВоВременноеХранилище
Если параметр не указан, помещенное значение будет удалено, после очередного запроса сервера из общего модуля, при контекстном и неконтекстном серверном вызове из формы, при серверном вызове из модуля команды или при получении формы.
Там даже приведён код с пометкой "Правильно:" вызов без уникального идентификатора. После этого вызова идёт всего один серверный вызов. И прямо указывается, что код корректный, но если нужно сделать несколько серверных вызова с сохранением адреса, то нужно использовать уникальный идентификатор! Почитайте ссылку.
Пока набирал сообщение уходил на обед и оставил точку останова в том же месте (значение уже помещено во временное хранилище, но на клиенте ещё не получено). Вернулся с обеда, нажал F5 - всё работает.
Еще могут отработать подписки на события. Сам сталкивался давным давно с этим, когда начинал работать со временным хранилищем
Вообще, передача объектов с клиента на сервер у 1С реализвона хреново. Например, из клиентского общего модуля нельзя получить имена реквизитов формы, тк нельзя передать форму с клиента на сервер, если ты не находишься собственно в самой форме. А процедура ПолучитьРеквизиты для УФ работает только на сервере. И тут никакие временные хранилища не помогут уже
(8) w.r., Ну уж за час у нас точно куча подписок было отработано в рабочей базе))
Давайте на минуту представим, что
Если параметр не указан, помещенное значение будет удалено, после очередного запроса сервера из общего модуля, при контекстном и неконтекстном серверном вызове из формы, при серверном вызове из модуля команды или при получении формы.
мы понимаем как
любое выполнение регламентного задания или иных действий параллельно работающих пользователей, использующих общие модули, в промежутке выполнения вашего кода на форме очистит временное хранилище
Первый вопрос: почему у меня тогда ничего не удалилось ни в течении часа ни в течении 10 минут на рабочей базе БП 3.0?
Второе: методы "ПоместитьФайл, ПоместитьФайлы, ПоместитьВоВременноеХранилище" получается вообще некорректно использовать без Уникального идентификатора, т.к. любое действие любого пользователя базы может привести к очистке адреса хранилища. На большом количестве народу (1000+ одновременно работающих пользователей) данные методы будут давать корректный (не очищенный) адрес только в 50% случаев, а то и меньше. Не думаю, что это так, тем более я на практике привёл пару исчерпывающих тестов.
У меня: Бухгалтерия предприятия, редакция 3.0 (3.0.41.64) , 1С:Предприятие 8.3 (8.3.6.2332).
(13) w.r., конечно клиент-серверный. Как я написал в статье для файловой базы можно вообще не заморачиваться, выполнил на сервере запрос, чтобы получить данные, получил Word (не важно откуда) заполнил и показал пользователю. Ничего передавать на клиент не надо, лепота))
Один вопрос - когда помещаете во временнное хранилище не указываете адрес:
Если Word Тогда
//Помещать во временное хранилище не обязательно
Возврат ПоместитьВоВременноеХранилище(СтруктураВсехДокументов);
Иначе
Возврат ТабДокумент;
КонецЕсли;
Тип: Произвольный.
Данные, которые необходимо поместить во временное хранилище.
<Адрес> (необязательный)
Тип: УникальныйИдентификатор; Строка.
Уникальный идентификатор формы, во временное хранилище которой надо поместить данные и вернуть новый адрес. Или адрес во временном хранилище, по которому надо поместить данные. Адрес должен получен ранее с помощью данного метода.
В случае, если передается УникальныйИдентификатор формы или адрес в хранилище, то значение будет автоматически удалено после закрытия этой формы.
Если передан УникальныйИдентификатор, не являющийся уникальным идентификатором формы, то значение будет удалено после завершения сеанса пользователя.
Если параметр не указан, помещенное значение будет удалено после очередного запроса сервера из общего модуля, при контекстном и неконтекстном серверном вызове из формы, при серверном вызове из модуля команды или при получении формы.
В макет у меня попадает ОболочкаActiveDocument, а Получить() падает с ошибкой вызова метода контекста.
В СП написано, что для этого типа метод Получить() доступен только на толстом клиенте.
(15) Kirill_K, у меня рабочего кода на руках нет, ибо сейчас в отпуске. Сделал обработку (см вложение). где всего пару строк кода. У меня дома на 8.2 (файловая база) не падает на методе "Получить()"! На работе практически самая свежая 8.3 и там тоже никакой ошибки на этой строчке (файловая или клиент-серверная).
Тупо в обработке нажмите на "Команда1" - будет ошибка?
(17) Kirill_K, код который написан у меня в статье я писал для клиент-серверной 8.3.6. Всё работало. Поэтому после обновления возможно и у вас заработает. Но для чистоты эксперимента я когда выйду на работу попробую на клиент-серверной обработку из 16 сообщения.
(23) micha26,
1. У вас офис на сервере установлен?
2. Если да, то на пользователе под которым работает агент 1С нормально запускается?
3. Обработка из 16-го сообщения работает?
(17) Kirill_K, попробовал в серверном режиме на 8.3.6.2332: ничего не вырубается. После строчки:
MSWord = Макет.Получить();
в переменной MSWord появляется COMОбъект. Походу Вам правильный совет - это обновиться.
Кстати в справке даже в 8.3.6.2332 написано, что метод Получить() якобы работает только в толстом клиенте:)
Возможная ошибка на сервере при вызове метода SaveAs:
Ошибка при вызове метода контекста (SaveAs)
Документ.SaveAs(ИмяВрем);
по причине:
Произошла исключительная ситуация (Microsoft Word): Ошибка команды
Мне поначалу эту ошибку не удалось победить, поэтому я стал использовать ДвоичныеДанные. Позже нашёл решение проблемы: необходимо по пути C:\Windows\SysWOW64\config\systemprofile\ и C:\Windows\System32\config\systemprofile\ создать папки Desktop. Туда никаких файлов никто не пишет, похоже, программе важен факт наличия этой папки. Решение нашёл по ссылке: http://devtrainingforum.v8.1c.ru/forum/thread.jsp?id=581998&threadtype=0
(29) micha26, даже не знаю что тогда... Такое ощущение, что проблема не на стороне 1С. Может антивирус или брандмауэр что-то блокирует? Попробуйте хотя-бы локально все по-вырубать и на клиенте SaveAS() сделать.
//На момент исполнения кода в переменной ПутьКФайлу содержится значение:"D:\Doc\Разовый договор.doc"
ФайлWord = ПолучитьИзВременногоХранилища(ПутьКФайлу);
Вместо ПутьКФайлу вам нужно использовать СтруктураДанных
&НаКлиенте
Процедура ПечатьРазовогоДоговора(Команда)
ПутьКФайлу = "D:\Doc\Разовый договор.doc";
СтруктураДанных = ПолучитьСтруктуруДанных();
ФайлWord = ПолучитьИзВременногоХранилища(СтруктураДанных);
ФайлWord.Записать(ПутьКФайлу); // Файл записывается в папку
Попытка
Word = Новый COMОбъект("Word.Application");
Исключение
Сообщить(ОписаниеОшибки());
Возврат;
КонецПопытки;
Договор = Word.Open(ПутьКФайлу); // Здесь останавливается Word.Open - метод объекта не обнаружен в принципе можно убрать на фиг.
КонецПроцедуры
Показать
Что-то я нить потерял, как теперь данные заполнить в документе.
(34) Вообщем переделал все на фиг - сохраняю шаблоны в выделенную папку на сервере, делаю запрос на сервере, сохраняю значения в реквизиты для печати и на клиенте заменой загоняю значения в шаблон. Шустро работает.
(34) micha26,
Вместо Word = Новый COMОбъект("Word.Application"); и Open(ПутьКФайлу); можно использовать Документ = ПолучитьCOMОбъект(ИмяВрем); (как в принципе и написано у меня в статье)
ФайлWord.Записать(ПутьКФайлу); // Файл записывается в папку
Попытка
Word = Новый COMОбъект("Word.Application");
Исключение
Сообщить(ОписаниеОшибки());
Возврат;
КонецПопытки;
Договор = Word.Open(ПутьКФайлу);
ИмяВрем = ПолучитьИмяВременногоФайла(".docx");
ДвоичныеДанныеМакета.Записать(ИмяВрем);
Попытка
Документ = ПолучитьCOMОбъект(ИмяВрем);
Что касается метода Open: он вроде используется так: Word.Documents.Open(ПутьКФайлу).
Прекрасная статья! Всё понятно и доходчиво написано.
По правде говоря мне пришло уведомление на гневный комментарий в моей теме со ссылкой на вашу статью где меня "ну просто опустили" (дословная цитата). Странно, прочитал всё от корки до корки, нигде никто меня не опускал)))) Гневный комментарий тоже удалён))) Видимо его автор сильно неуверен в себе, раз позволяет себе такое.
Насчёт кода на сервере, которым я записывал файл, вот он (в начале лета приводил в порядок всю свою базу наработок):
//Получает макет ActiveDocument и записывает его в файл
Функция ПолучитьМакет(Документ) Экспорт
Каталог = ПараметрыСеанса.ТекущийПользователь.РабочийКаталог;
Каталог = ?(Прав(Каталог,1) = "\", Каталог, Каталог+"\");
Если ЗначениеЗаполнено(Каталог) Тогда
Попытка
ПолноеИмяФайла = Каталог+Документ.Номер+".doc";
Макет = Документы.ДоговорыКонтрагентов.ПолучитьМакет("ActiveDocument");
Макет.Записать(ПолноеИмяФайла);
Возврат ПолноеИмяФайла;
Исключение
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Не удалось записать файл "+ОписаниеОшибки();
Сообщение.Сообщить();
Возврат Неопределено;
КонецПопытки;
Иначе
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Не указан каталог. Сохранение файла невозможно";
Сообщение.Сообщить();
Возврат Неопределено;
КонецЕсли;
КонецФункции // ()
Показать
Спасибо, что поставили ссылку на статью. Рад, что хоть чем-то смог помочь. Сейчас отредактирую её и приведу данный листинг кода.
Так вот как оказывается можно ещё сохранить! Не заметил, что у ОболочкаActiveDocument есть метод Записать. Обязательно обновите статью, т.к. многие сваливаются на методе SaveAs, а Записать() у ОболочкаActiveDocument фактически является альтернативой