Vinicius,
Como eu falei, o binário acaba tendo problemas com certos textos, veja esse exemplo:
#include "protheus.ch"
#include "fileio.ch"
//-------------------------------------------------------------------
/*/{Protheus.doc} u_LenStr
Função de exemplo do funcionamento do MemoRead e Len em comparação
a leitura do arquivo, byte a byte
@author Daniel Mendes
@since 21/07/2020
@version 1.0
/*/
//-------------------------------------------------------------------
function u_LenStr()
local cPDF as char
local cData as char
local nLength as numeric
local nHandle as numeric
cPDF := "mypdf.pdf"
cData := MemoRead(cPDF)
nHandle := FOpen(cPDF)
nLength := FSeek(nHandle, 0, FS_END)
FClose(nHandle)
//Veja que a diferença pode ser bem grande, o teste foi feito com um PDF de cerca de 580Kb
ConOut("Tamanho lento a string: " + cValToChar(Len(cData)))
ConOut("Tamanho lento o arquivo: " + cValToChar(nLength))
return
Esse teste foi executado com um arquivo que tem mais de 580Kb e mais que 600000 caracteres, porém o appserver me retorna que o tamanho da string é de 65535 na função Len
, gerando diversas inconsistências.
Após muitos testes, consegui chegar a um resultado equivalente ao Python.
Vamos, lá no Python eu criei o seguinte código:
with open("mypdf.pdf", "rb") as f, open("python-bytes.txt", "w") as ob, open("python-bytes-zero.txt", "w") as obz:
numbers = [int(w) for line in f.readlines() for w in line]
ob.write("".join([str(n) for n in numbers]))
obz.write("".join([str(n).zfill(3) for n in numbers]))
Então, eu abro o arquivo PDF e gero o array de bytes, depois escrevo os mesmo em um arquivo para futura comparação com os resultados do ADVPL.
Como eu citei, a função Len
acaba não tendo o retorno correto, então o que podemos fazer é ler o arquivo byte a byte com a FRead
:
//-------------------------------------------------------------------
/*/{Protheus.doc} u_testBytesFile
Função para testes das demais funções de conversão de arquivo para
array (byte e binário)
@author Daniel Mendes
@since 21/07/2020
@version 1.0
/*/
//-------------------------------------------------------------------
function u_testBytesFile()
local cFile as char
local aBytes as array
local aBinary as array
local aBytesZeros as array
cFile := "mypdf.pdf"
aBytes := u_file2ArraryByte(cFile)
aBinary := u_file2BinaryArray(cFile)
aBytesZeros := u_file2ByteArray(cFile)
writeFile("advpl-bytes.txt", aBytes)
writeFile("advpl-binary.txt", aBinary)
writeFile("advpl-bytes-zero.txt", aBytesZeros)
cleanArray(@aBytes)
cleanArray(@aBinary)
cleanArray(@aBytesZeros)
return
//-------------------------------------------------------------------
/*/{Protheus.doc} writeFile
Cria um arquivo com base no nome e array de valores recebido
@param cFile Arquivo que será criado
@param aValues Array contendo os dados para a escrita
@author Daniel Mendes
@since 21/07/2020
@version 1.0
/*/
//-------------------------------------------------------------------
static function writeFile(cFile as char, aValues as array)
local nHandle as numeric
nHandle := FCreate(cFile)
AEval(aValues,{|cValue| FWrite(nHandle, cValue)})
FClose(nHandle)
return
//-------------------------------------------------------------------
/*/{Protheus.doc} cleanArray
Limpa o conteúdo do array recebido
@param aArray Array que será limpo
@author Daniel Mendes
@since 21/07/2020
@version 1.0
/*/
//-------------------------------------------------------------------
static function cleanArray(aArray as array)
aSize(aArray, 0)
aArray := nil
return
//-------------------------------------------------------------------
/*/{Protheus.doc} u_file2ArraryByte
Converte o arquivo recebido para um array de bytes
@param cFile Arquivo que será convertido para o array de bytes
@param aBytes Array contendo os dados em bytes
@author Daniel Mendes
@since 21/07/2020
@version 1.0
/*/
//-------------------------------------------------------------------
function u_file2ArraryByte(cFile as char) as array
local cByte as char
local aBytes as array
local nLoop as numeric
local nLength as numeric
local nHandle as numeric
nHandle := openFileSize(cFile, @nLength)
aBytes := {}
for nLoop := 1 to nLength
cByte := FReadStr(nHandle, 1)
cByte := cValToChar(Asc(cByte))
aAdd(aBytes, cByte)
next
FClose(nHandle)
return aBytes
//-------------------------------------------------------------------
/*/{Protheus.doc} u_file2ByteArray
Converte o arquivo recebido para um array de bytes
@param cFile Arquivo que será convertido para o array de bytes com zeros
@param aBytes Array contendo os dados em bytes, com zeros a esquerda
@author Daniel Mendes
@since 21/07/2020
@version 1.0
/*/
//-------------------------------------------------------------------
function u_file2ByteArray(cFile as char) as array
local cByte as char
local aBytes as array
local nLoop as numeric
local nLength as numeric
local nHandle as numeric
nHandle := openFileSize(cFile, @nLength)
aBytes := {}
for nLoop := 1 to nLength
cByte := FReadStr(nHandle, 1)
cByte := StrZero(Asc(cByte), 3)
aAdd(aBytes, cByte)
next
FClose(nHandle)
return aBytes
//-------------------------------------------------------------------
/*/{Protheus.doc} u_file2BinaryArray
Converte o arquivo recebido para um array de binários
@param cFile Arquivo que será convertido para o array de binários
@return aBynary Array contendo os dados em valores binários
@author Daniel Mendes
@since 21/07/2020
@version 1.0
/*/
//-------------------------------------------------------------------
function u_file2BinaryArray(cFile as char) as array
local cBinary as char
local aBynary as array
local nLoop as numeric
local nLength as numeric
local nHandle as numeric
nHandle := openFileSize(cFile, @nLength)
aBynary := {}
for nLoop := 1 to nLength
cBinary := FReadStr(nHandle, 1)
cBinary := Bin2Str(cBinary)
cBinary := StrTran(cBinary, " ", "0")
cBinary := StrTran(cBinary, "x", "1")
aAdd(aBynary, cBinary)
next
FClose(nHandle)
return aBynary
//-------------------------------------------------------------------
/*{Protheus.doc} openFileSize
Função auxiliar para abrir o arquivo e descobrir o seu tamanho
@param cFile Arquivo que será aberto
@param nLength Tamanho do arquivo [Referência]
@param nHandle Handle do arquivo aberto
@author Daniel Mendes
@since 21/07/2020
@version 1.0
*/
//-------------------------------------------------------------------
static function openFileSize(cFile as char, nLength as numeric) as numeric
local nHandle as numeric
nHandle := FOpen(cFile, FO_READ+FO_SHARED)
nLength := FSeek(nHandle, 0, FS_END)
FSeek(nHandle, 0)
return nHandle
As principais funções do exemplo são:
- u_file2ArraryByte - Converte o arquivo para bytes sem qualquer tratamento
- u_file2ByteArray - Converte o arquivo para bytes tendo sempre três caracteres, criei esse exemplo por conta de vários exemplos e conversores online que trabalham assim
- u_file2BinaryArray - Converte o arquivo para binários, apenas um outro exemplo de como pode ser tratado
Como comentei, fiz os testes me baseando em Python e tiveram resultados satisfatórios, mas... A performance ficou baixíssima, apesar dos exemplos, recomendo que você trabalhe com base64 que é uma função em C++ com uma performance muito superior, já tendo sido homologada e utilizada em diversos projetos.
Bom dia Vinicius, o serviço que vai receber o arquivo, também é seu ou é de terceiros?
— Daniel Mendes 05 de Feb de 2020Temos os 2 casos Daniel
— VINICIUS GATI 05 de Feb de 2020Recomendo você fazer isso, transformando o arquivo para base64 com a função Encode64, enviando no header essa informação de encoded, esse tipo de leitura com ASC não vai funcionar.
— Daniel Mendes 05 de Feb de 2020Base64 funciona tranquilo mesmo, agora do outro jeito não tem como mesmo? tentei com memoread e fread tenho o mesmo resultado errado.
— VINICIUS GATI 05 de Feb de 2020Arquivos PDF tem vários tipos de caracteres, porém caso exista um caractere \0 (NUL), você terá problemas, pois o binário do Protheus é feito em C++, logo ele encara esse caractere como final de string.
— Daniel Mendes 05 de Feb de 2020