В инфраструктурах, активно использующих возможности web-сервисов 1С, нередко возникает потребность получения не только массивов данных из базы, но и различных файлов. Сам файл через веб-сервис так просто не передашь, но на его основе можно создать объект ДвоичныеДанные, который, в свою очередь, уже сериализуется в base64Binary. После такой операции наш веб-сервис без проблем передает данные и получателю остается только выполнить на своей стороне обратное преобразование и сохранить файл.
В качестве примера такого механизма я буду создавать в 1С счет на оплату на основании переданных через сервис ИНН контрагента и суммы, и возвращать PDF печатной формы. На этот раз, кроме примеров кода, дополнительно был записан скринкаст разработки.
Для начала создадим в пустой конфигурации справочник "Контрагенты" с реквизитом "ИНН" и документ "Счет" с реквизитами "Контрагент" и "СуммаДокумента".
Теперь добавим пакет XDTO "test_ws"с пространством имен "http://www.test-ws.org", в котором опишем тип объекта "File". Он будет содержать свойства:
binaryData - (base64Binary (http://www.w3.org/2001/XMLSchema));
ext - string (http://www.w3.org/2001/XMLSchema);
message - string (http://www.w3.org/2001/XMLSchema).
Создаем web-сервис "DocWork", пространство имен "http://www.test-ws.org", выбираем пакет "http://www.test-ws.org". Добавляем в сервис операцию "CreateBill", возвращаемый тип File (http://www.test-ws.org), входные параметры:
INN - string (http://www.w3.org/2001/XMLSchema);
Summ - decimal (http://www.w3.org/2001/XMLSchema).
Код web-сервиса:
Код 1C v 8.3 Функция CreateBill(INN, Summ)
typeFile = ФабрикаXDTO.Тип("<a href="http://www.test-ws.org" ,"="">http://www.test-ws.org",</a> "File");
текФайл = ФабрикаXDTO.Создать(typeFile);
текФайл.binaryData = ФабрикаXDTO.Создать(ФабрикаXDTO.Тип("<a href="http://www.w3.org/2001/XMLSchema" ,"="">http://www.w3.org/2001/XMLSchema",</a> "base64Binary"));
текФайл.ext = ФабрикаXDTO.Создать(ФабрикаXDTO.Тип("<a href="http://www.w3.org/2001/XMLSchema" ,"="">http://www.w3.org/2001/XMLSchema",</a> "string"));
Если ПустаяСтрока(INN) ИЛИ Summ = 0 Тогда
текФайл.message = "empty data";
Возврат текФайл;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Контрагенты.Ссылка
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.ИНН = &ИНН";
Запрос.УстановитьПараметр("ИНН", INN);
Результат = Запрос.Выполнить().Выгрузить();
Если Результат.Количество() = 0 Тогда
текФайл.message = "client not found";
Возврат текФайл;
КонецЕсли;
СчетДок = Документы.Счет.СоздатьДокумент();
СчетДок.Дата = ТекущаяДата();
СчетДок.Контрагент = Результат[0].Ссылка;
СчетДок.СуммаДокумента = Summ;
СчетДок.Записать();
ТабДок = Новый ТабличныйДокумент;
Документы.Счет.Печать(ТабДок, СчетДок.Ссылка);
ИмяФайла = ПолучитьИмяВременногоФайла("pdf");
ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.PDF);
ДанныеФайла = Новый ДвоичныеДанные(ИмяФайла);
текФайл.binaryData = ДанныеФайла;
текФайл.message = "ok";
текФайл.ext = "pdf";
Возврат текФайл;
КонецФункции<br>
Тестовая обработка
Код 1C v 8.3 &НаСервере
Функция ПолучитьПДФ()
ОпределениеВС = Новый WSОпределения("<a href="http://localhost/testWS/ws/ws1.1cws?wsdl" )"="">http://localhost/testWS/ws/ws1.1cws?wsdl")</a>;
СсылкаВС = Новый WSПрокси(ОпределениеВС, "<a href="http://www.test-ws.org" ,"="">http://www.test-ws.org",</a> "DocWork", "DocWorkSoap");
ФайлСервис = СсылкаВС.CreateBill(ИНН, Сумма);
Возврат ФайлСервис.binaryData;
КонецФункции// ПолучитьПДФ()
&НаКлиенте
Процедура СоздатьСчет(Команда)
мДанные = ПолучитьПДФ();
Если мДанные = Неопределено Тогда
Возврат;
КонецЕсли;
мДанные.Записать("c:\temp_bil.pdf");
КонецПроцедуры<br>
А так же обещанное видео, как есть, без купюр
Источник
Наиболее эффективно это можно сделать с помощью объектов OLE Automation , работа с которыми осуществляется при помощи:
sp_OACreate, sp_OAGetProperty, sp_OASetProperty, sp_OAMethod, sp_OAGetErrorInfo, sp_OADestroy (подробное описание есть в http://technet.microsoft.com/ru-ru/library/ms203721(sql.90).aspx).
Рабочий пример процедуры (была написана для сохранения zip-архивов, хранящихся в базе):
Код CREATE PROCEDURE dbo.SaveRequestDataAsFile (
@FileName varchar(1024) --имя файла
,@FilePath varchar(1024) --путь файла
,@ReqID --некое условие на таблицу
)
AS
BEGIN
DECLARE
@Stream integer,
@Buffer varbinary(4096),
@Size integer,
@Pos integer,
@BufSize integer,
@FileNameLocal varchar(1024),
@HR integer
if RIGHT(@FilePath, 1)<>'&# 092;' begin set @FilePath=@FilePath+'&# 092;' end
set @FileNameLocal = @FilePath + @FileName
SET @BufSize = 4096
EXEC @HR = sp_OACreate 'ADODB.Stream',@Stream OUT
if @HR<>0
begin
--обработка ошибки создания объекта
EXEC [dbo].[sp_displayoaerrorinfo] @Stream ,@HR
end
EXEC @HR = sp_OASetProperty @Stream,'Type',1 -- binary
EXEC @HR = sp_OASetProperty @Stream,'Mode',3 -- write|read
EXEC @HR = sp_OAMethod @Stream,'Open'
S_elect
@Size = DATALENGTH(SrvRequests.RequestData)
FROM SrvRequests WHERE SrvRequests.RequestID = @ReqID
Set @Pos=0
WHILE @Pos < @Size BEGIN
SET @BufSize = CASE WHEN @Size - @Pos < 4096 THEN @Size - @Pos ELSE 4096 END
S_elect @Buffer = substring(SrvRequests.RequestData ,@Pos+1, @BufSize)
from SrvRequests where SrvRequests.RequestID = @ReqID
EXEC @HR = sp_OAMethod @Stream, 'Write', NULL, @Buffer
SET @Pos = @Pos + @BufSize
END
EXEC @HR = sp_OAMethod @Stream,'SaveToFile',null, @FileNameLocal,2
EXEC @HR = sp_OAMethod @Stream,'Close'
EXEC @HR = sp_OADestroy @Stream
END
Рекомендуется создать еще пару процедур для внятного описания ошибок в случае их возникновения
Код CREATE PROCEDURE sp_hexadecimal
@binvalue varbinary(255),
@hexvalue varchar(255) OUTPUT
AS
DECLARE @charvalue varchar(255)
DECLARE @i int
DECLARE @length int
DECLARE @hexstring char(16)
S_elect @charvalue = '0x'
S_elect @i = 1
S_elect @length = DATALENGTH(@binvalue)
S_elect @hexstring = '0123456789abcdef'
WHILE (@i <= @length)
BEGIN
DECLARE @tempint int
DECLARE @firstint int
DECLARE @secondint int
S_elect @tempint = CONVERT(int, SUBSTRING(@binvalue,@i,1))
S_elect @firstint = FLOOR(@tempint/16)
S_elect @secondint = @tempint - (@firstint* 16)
S_elect @charvalue = @charvalue +
SUBSTRING(@hexstring, @firstint+1, 1) +
SUBSTRING(@hexstring, @secondint+1, 1)
S_elect @i = @i + 1
END
----------------------------------------------------------------------
CREATE PROCEDURE sp_displayoaerrorinfo
@object int,
@hresult int
AS
DECLARE @output varchar(255)
DECLARE @hrhex char(10)
DECLARE @hr int
DECLARE @source varchar(255)
DECLARE @description varchar(255)
PRINT 'OLE Automation Error Information'
EXEC sp_hexadecimal @hresult, @hrhex OUT
S_elect @output = ' HRESULT: ' + @hrhex
PRINT @output
EXEC @hr = sp_OAGetErrorInfo @object, @source OUT, @description OUT
IF @hr = 0
BEGIN
S_elect @output = ' Source: ' + @source
PRINT @output
S_elect @output = ' Description: ' + @description
PRINT @output
END
ELSE
BEGIN
PRINT ' sp_OAGetErrorInfo failed.'
RETURN
END