No vídeo de hoje, vamos demonstrar em como enviar um arquivo (PDF) via REST usando AdvPL.
A dúvida de hoje, nos perguntaram, como seria possível enviar um arquivo via WebService REST.
Pensando nisso, montamos esse exemplo, onde demonstramos em como gerar um relatório e enviar ele em Base64.
Segue abaixo o vídeo exemplificando:
E abaixo o código fonte desenvolvido:
//Bibliotecas #Include "Totvs.ch" #Include "TopConn.ch" #Include "RPTDef.ch" #Include "FWPrintSetup.ch" #Include "RESTFul.ch" //Alinhamentos #Define PAD_LEFT 0 #Define PAD_RIGHT 1 #Define PAD_CENTER 2 #Define PAD_JUSTIFY 3 //Opção disponível somente a partir da versão 1.6.2 da TOTVS Printer //Cor(es) Static nCorCinza := RGB(110, 110, 110) Static nCorLinha := RGB(165, 255, 203) /*/{Protheus.doc} User Function zVid0096 Lista de Produtos @author Atilio @since 20/03/2024 @version 1.0 @type function @obs Codigo gerado automaticamente pelo Autumn Code Maker @see http://autumncodemaker.com /*/ User Function zVid0096(cProdDe, cProdAte, lAuto, cPasta, cArquivo) Local aArea := FWGetArea() Local aPergs := {} Default cProdDe := Space(15) Default cProdAte := StrTran(cProdDe, " ", "Z") Default lAuto := .F. Default cPasta := Iif(IsBlind(), "\spool\", GetTempPath()) Default cArquivo := 'zVid0096'+RetCodUsr()+'_' + dToS(Date()) + '_' + StrTran(Time(), ':', '-') + '.pdf' Private lAutoPDF := lAuto Private cPastaPDF := cPasta Private cArquiPDF := cArquivo //Se for PDF automático, seta os parâmetros em memória e já imprime If lAutoPDF MV_PAR01 := cProdDe MV_PAR02 := cProdAte Processa({|| fImprime()}) //Senão, vai exibir uma tela de parâmetros Else //Adicionando os parametros do ParamBox aAdd(aPergs, {1, "Produto De", cProdDe, "", ".T.", "SB1", ".T.", 80, .F.}) aAdd(aPergs, {1, "Produto Até", cProdAte, "", ".T.", "SB1", ".T.", 80, .T.}) //Se a pergunta for confirma, cria o relatorio If ParamBox(aPergs, 'Informe os parâmetros', /*aRet*/, /*bOk*/, /*aButtons*/, /*lCentered*/, /*nPosx*/, /*nPosy*/, /*oDlgWizard*/, /*cLoad*/, .F., .F.) Processa({|| fImprime()}) EndIf EndIf FWRestArea(aArea) Return /*/{Protheus.doc} fImprime Faz a impressão do relatório zVid0096 @author Atilio @since 20/03/2024 @version 1.0 @type function @obs Codigo gerado automaticamente pelo Autumn Code Maker @see http://autumncodemaker.com /*/ Static Function fImprime() Local aArea := FWGetArea() Local nTotAux := 0 Local nAtuAux := 0 Local cQryAux := '' Private oPrintPvt Private oBrushLin := TBrush():New(,nCorLinha) Private cHoraEx := Time() Private nPagAtu := 1 Private cLogoEmp := fLogoEmp() //Linhas e colunas Private nLinAtu := 0 Private nLinFin := 800 Private nColIni := 010 Private nColFin := 580 Private nColMeio := (nColFin-nColIni)/2 //Colunas dos relatorio Private nColDad1 := nColIni Private nColDad2 := nColIni + 60 Private nColDad3 := nColIni + 180 Private nColDad4 := nColIni + 240 Private nColDad5 := nColIni + 300 //Declarando as fontes Private cNomeFont := 'Arial' Private oFontDet := TFont():New(cNomeFont, /*uPar2*/, -11, /*uPar4*/, .F., /*uPar6*/, /*uPar7*/, /*uPar8*/, /*uPar9*/, .F.) Private oFontDetN := TFont():New(cNomeFont, /*uPar2*/, -13, /*uPar4*/, .T., /*uPar6*/, /*uPar7*/, /*uPar8*/, /*uPar9*/, .F.) Private oFontRod := TFont():New(cNomeFont, /*uPar2*/, -8, /*uPar4*/, .F., /*uPar6*/, /*uPar7*/, /*uPar8*/, /*uPar9*/, .F.) Private oFontMin := TFont():New(cNomeFont, /*uPar2*/, -7, /*uPar4*/, .F., /*uPar6*/, /*uPar7*/, /*uPar8*/, /*uPar9*/, .F.) Private oFontTit := TFont():New(cNomeFont, /*uPar2*/, -15, /*uPar4*/, .T., /*uPar6*/, /*uPar7*/, /*uPar8*/, /*uPar9*/, .F.) //Monta a consulta de dados cQryAux += "SELECT B1_COD, B1_DESC, B1_UM, B1_TIPO " + CRLF cQryAux += "FROM " + RetSQLName("SB1") + " SB1 " + CRLF cQryAux += "WHERE B1_FILIAL = '" + FWxFilial("SB1") + "' " + CRLF cQryAux += "AND B1_MSBLQL != '1' " + CRLF cQryAux += "AND B1_COD >= '" + MV_PAR01 + "' " + CRLF cQryAux += "AND B1_COD <= '" + MV_PAR02 + "' " + CRLF cQryAux += "AND SB1.D_E_L_E_T_ = ' ' " + CRLF cQryAux += "ORDER BY B1_COD" + CRLF PLSQuery(cQryAux, 'QRY_AUX') //Define o tamanho da régua DbSelectArea('QRY_AUX') QRY_AUX->(DbGoTop()) Count to nTotAux ProcRegua(nTotAux) QRY_AUX->(DbGoTop()) //Somente se tiver dados If ! QRY_AUX->(EoF()) //Se veio de processo automático If lAutoPDF oPrintPvt := FWMSPrinter():New(; cArquiPDF,; // cFilePrinter IMP_PDF,; // nDevice .F.,; // lAdjustToLegacy '',; // cPathInServer .T.,; // lDisabeSetup .F.,; // lTReport ,; // oPrintSetup ,; // cPrinter .T.,; // lServer .T.,; // lParam10 ,; // lRaw .F.; // lViewPDF ) Else //Criando o objeto de impressao oPrintPvt := FWMSPrinter():New(; cArquiPDF,; // cFilePrinter IMP_PDF,; // nDevice .F.,; // lAdjustToLegacy ,; // cPathInServer .T.,; // lDisabeSetup ,; // lTReport @oPrintPvt,; // oPrintSetup ,; // cPrinter ,; // lServer ,; // lParam10 ,; // lRaw .T.; // lViewPDF ) EndIf oPrintPvt:cPathPDF := cPastaPDF oPrintPvt:SetResolution(72) oPrintPvt:SetPortrait() oPrintPvt:SetPaperSize(DMPAPER_A4) oPrintPvt:SetMargin(0, 0, 0, 0) //Imprime os dados fImpCab() While ! QRY_AUX->(EoF()) nAtuAux++ IncProc('Imprimindo registro ' + cValToChar(nAtuAux) + ' de ' + cValToChar(nTotAux) + '...') //Se atingiu o limite, quebra de pagina fQuebra() //Faz o zebrado ao fundo If nAtuAux % 2 == 0 oPrintPvt:FillRect({nLinAtu - 2, nColIni, nLinAtu + 12, nColFin}, oBrushLin) EndIf //Imprime a linha atual oPrintPvt:SayAlign(nLinAtu, nColDad1, Alltrim(QRY_AUX->B1_COD), oFontDetN, 60, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/) oPrintPvt:SayAlign(nLinAtu, nColDad2, Alltrim(QRY_AUX->B1_DESC), oFontDet, 120, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/) oPrintPvt:SayAlign(nLinAtu, nColDad3, Alltrim(QRY_AUX->B1_UM), oFontDet, 60, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/) oPrintPvt:SayAlign(nLinAtu, nColDad4, Alltrim(QRY_AUX->B1_TIPO), oFontDet, 60, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/) nLinAtu += 15 oPrintPvt:Line(nLinAtu-3, nColIni, nLinAtu-3, nColFin, nCorCinza) //Se atingiu o limite, quebra de pagina fQuebra() QRY_AUX->(DbSkip()) EndDo //Imprime o último rodapé fImpRod() //Se for automático, aciona o método Print ao invés do Preview If lAutoPDF oPrintPvt:Print() Else oPrintPvt:Preview() EndIf Else FWAlertError('Não foi encontrado informações com os parâmetros informados!', 'Atenção') EndIf QRY_AUX->(DbCloseArea()) FWRestArea(aArea) Return /*/{Protheus.doc} fLogoEmp Função que retorna o logo da empresa conforme configuração da DANFE @author Atilio @since 20/03/2024 @version 1.0 @type function @obs Codigo gerado automaticamente pelo Autumn Code Maker @see http://autumncodemaker.com /*/ Static Function fLogoEmp() Local cLogo := '\x_imagens\logo.png' Return cLogo /*/{Protheus.doc} fImpCab Função que imprime o cabeçalho do relatório @author Atilio @since 20/03/2024 @version 1.0 @type function @obs Codigo gerado automaticamente pelo Autumn Code Maker @see http://autumncodemaker.com /*/ Static Function fImpCab() Local cTexto := '' Local nLinCab := 015 //Iniciando Pagina oPrintPvt:StartPage() //Imprime o logo If File(cLogoEmp) oPrintPvt:SayBitmap(005, nColIni, cLogoEmp, 030, 030) EndIf //Cabecalho cTexto := 'Lista Produtos' oPrintPvt:SayAlign(nLinCab, nColMeio-200, cTexto, oFontTit, 400, 20, /*nClrText*/, PAD_CENTER, /*nAlignVert*/) //Linha Separatoria nLinCab += 020 oPrintPvt:Line(nLinCab, nColIni, nLinCab, nColFin) //Atualizando a linha inicial do relatorio nLinAtu := nLinCab + 5 If nPagAtu == 1 //Imprimindo os parâmetros cTexto := MV_PAR01 oPrintPvt:SayAlign(nLinAtu, nColIni, 'Produto De', oFontDetN, 200, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/) oPrintPvt:SayAlign(nLinAtu, nColIni+200, cTexto, oFontDet, 200, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/) nLinAtu += 15 cTexto := MV_PAR02 oPrintPvt:SayAlign(nLinAtu, nColIni, 'Produto Até', oFontDetN, 200, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/) oPrintPvt:SayAlign(nLinAtu, nColIni+200, cTexto, oFontDet, 200, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/) nLinAtu += 15 oPrintPvt:Line(nLinAtu-3, nColIni, nLinAtu-3, nColFin, nCorCinza) nLinAtu += 5 EndIf oPrintPvt:SayAlign(nLinAtu, nColDad1, 'Código', oFontMin, 60, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/) oPrintPvt:SayAlign(nLinAtu, nColDad2, 'Descrição', oFontMin, 120, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/) oPrintPvt:SayAlign(nLinAtu, nColDad3, 'Unid.Medida', oFontMin, 60, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/) oPrintPvt:SayAlign(nLinAtu, nColDad4, 'Tipo', oFontMin, 60, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/) nLinAtu += 15 Return /*/{Protheus.doc} fImpRod Função que imprime o rodapé e encerra a página @author Atilio @since 20/03/2024 @version 1.0 @type function @obs Codigo gerado automaticamente pelo Autumn Code Maker @see http://autumncodemaker.com /*/ Static Function fImpRod() Local nLinRod:= nLinFin Local cTexto := '' //Linha Separatoria oPrintPvt:Line(nLinRod, nColIni, nLinRod, nColFin) nLinRod += 3 //Dados da Esquerda cTexto := dToC(dDataBase) + ' ' + cHoraEx + ' ' + FunName() + ' (zVid0096) ' + UsrRetName(RetCodUsr()) oPrintPvt:SayAlign(nLinRod, nColIni, cTexto, oFontRod, 500, 10, /*nClrText*/, PAD_LEFT, /*nAlignVert*/) //Direita cTexto := 'Pagina '+cValToChar(nPagAtu) oPrintPvt:SayAlign(nLinRod, nColFin-40, cTexto, oFontRod, 040, 10, /*nClrText*/, PAD_RIGHT, /*nAlignVert*/) //Finalizando a pagina e somando mais um oPrintPvt:EndPage() nPagAtu++ Return /*/{Protheus.doc} fQuebra Função que valida se a linha esta próxima do final, se sim quebra a página @author Atilio @since 20/03/2024 @version 1.0 @type function @obs Codigo gerado automaticamente pelo Autumn Code Maker @see http://autumncodemaker.com /*/ Static Function fQuebra() If nLinAtu >= nLinFin-10 fImpRod() fImpCab() EndIf Return // ----------------------------- //Exemplo URL: http://127.0.0.1:8400/rest/zWsRelProd/get_report?initID=00000&lastID=ZZZZZ //Obs.: Lembre-se de colocar o printer.exe dentro da pasta do AppServer do serviço REST /*/{Protheus.doc} WSRESTFUL zWsRelProd Relatório de Produtos @author Atilio @since 22/03/2024 @version 1.0 @type wsrestful @obs Codigo gerado automaticamente pelo Autumn Code Maker @see http://autumncodemaker.com /*/ WSRESTFUL zWsRelProd DESCRIPTION 'Relatório de Produtos' //Atributos WSDATA initID AS STRING WSDATA lastID AS STRING //Métodos WSMETHOD GET REPORT DESCRIPTION 'Retorna o registro pesquisado' WSSYNTAX '/zWsRelProd/get_report?{initID, lastID}' PATH 'get_report' PRODUCES APPLICATION_JSON END WSRESTFUL /*/{Protheus.doc} WSMETHOD GET REPORT Monta o relatório de produtos @author Atilio @since 22/03/2024 @version 1.0 @type method @param initID, Caractere, String com o código inicial do produto @param lastID, Caractere, String com o código finald do produto @obs Codigo gerado automaticamente pelo Autumn Code Maker @see http://autumncodemaker.com /*/ WSMETHOD GET REPORT WSRECEIVE initID, lastID WSSERVICE zWsRelProd Local lRet := .T. Local jResponse := JsonObject():New() Local cPasta := "\spool\" Local cArquivo := "produtos_" + dToS(Date()) + "_" + StrTran(Time(), ":", "-") + ".pdf" //Se o id estiver vazio If Empty(::initID) .And. Empty(::lastID) Self:setStatus(500) jResponse['errorId'] := 'ID001' jResponse['error'] := 'IDs vazios' jResponse['solution'] := 'Informe o codigo inicial e ou o codigo final' Else //Se a pasta não existir, cria ela If ! ExistDir(cPasta) MakeDir(cPasta) EndIf //Aciona a geração do PDF u_zVid0096(::initID, ::lastID, .T., cPasta, cArquivo) //Se o arquivo não foi encontrado If ! File(cPasta + cArquivo) Self:setStatus(500) jResponse['errorId'] := 'ID002' jResponse['error'] := 'Arquivo nao encontrado' jResponse['solution'] := 'Falha ao criar o arquivo PDF, contate o Administrador' Else //Define o retorno // Baixe a zFile64 disponível nesse link - https://terminaldeinformacao.com/2023/10/11/funcao-para-transformar-um-arquivo-em-string-base-64/ jResponse['pdfContent'] := u_zFile64(cPasta + cArquivo) jResponse['pdfName'] := cPasta + cArquivo EndIf EndIf //Define o retorno Self:SetContentType('application/json') Self:SetResponse(jResponse:toJSON()) Return lRet
Bom pessoal, por hoje é só.
Abraços e até a próxima.