Como enviar um PDF via REST usando AdvPL | Ti Responde 0096

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.

Dan (Daniel Atilio)
Cristão de ramificação protestante. Especialista em Engenharia de Software pela FIB, graduado em Banco de Dados pela FATEC Bauru e técnico em informática pelo CTI da Unesp. Entusiasta de soluções Open Source e blogueiro nas horas vagas. Autor e mantenedor do portal Terminal de Informação.

Deixe uma resposta

Terminal de Informação