Pular para o conteúdo principal
Esta seção fornece instruções sobre como implantar um aplicativo FRE 12 para Windows no Azure Cloud Service. Como exemplo, é apresentado um Worker que processa arquivos por meio de um contêiner em uma conta do Azure Storage. Para implementar este cenário, é utilizada uma Licença Online que se conecta ao servidor de licenças *.abbyy.com.
O Serviço de Licenciamento da ABBYY pode trabalhar com apenas uma Licença Online por vez.
A implantação do seu aplicativo no Cloud Service inclui várias etapas:
  1. Organizar sua Licença Online, máquina local e instância na nuvem usando os pré-requisitos
  2. Executar as etapas preparatórias antes de implantar seu aplicativo
  3. Implantar seu aplicativo no Cloud Service
Consulte os exemplos de código que ilustram como usar os métodos do ABBYY FineReader Engine para processamento de documentos.

Pré-requisitos

Licença Online

Para usar uma Licença Online, você deve receber as seguintes informações do setor de vendas: Você precisa atender às seguintes condições para usar uma Licença Online onde quer que o Serviço de Licenciamento ABBYY esteja instalado:
  • Conexão ativa com a Internet
  • Conexões permitidas para *.abbyy.com na porta 443 (HTTPS)
  • Certificado raiz GoDaddy para a Autoridade de Certificação (Ele deve ser instalado no repositório de certificados de Autoridades de Certificação Raiz Confiáveis da máquina local. Consulte as informações detalhadas sobre o certificado no site da GoDaddy).

máquina local

Antes de criar seu Cloud Service, use a seguinte especificação para configurar sua máquina local:
  • Visual Studio 2019 e seus módulos para desenvolvimento de aplicações para Azure (consulte Azure feature in Visual Studio ou use o Visual Studio Installer para baixar esses módulos)
  • Emuladores de Cloud Service e armazenamento para facilitar a depuração (veja as informações aqui e baixe via Visual Studio Installer)
  • Solução de template para Azure Cloud Service (pré-visualização estendida) com um único projeto de Worker role (veja as informações aqui)
  • .NET Framework 4.7.2
  • Azure SDK (baixe aqui)
  • Pacote NuGet para trabalhar com Azure Storage e blob containers - Azure.Storage.Blobs (baixe aqui)
  • Wrapper do ABBYY FineReader Engine para .NET Framework 4.7 (na pasta C:\ProgramData\ABBYY\SDK\12\FineReader Engine\Inc.NET interops após a developer installation)
  • Interface IFileWriter substituída para trabalhar com blob containers (veja o exemplo abaixo)
  • Azure Storage Explorer (opcional - baixe aqui)

Instância do Cloud Service

A instância do Cloud Service é usada para armazenar seu projeto WorkerRole no Azure. Use as seguintes especificações para organizar essa instância:
  • Versão do .NET Framework do seu serviço
  • PowerShell para implantação do ABBYY FineReader Engine
  • NuGet 2.8.5.201 ou superior para upload do Azure SDK
  • Azure SDK para trabalhar com uma conta do Azure Storage usando PowerShell

Etapas preparatórias

As etapas preparatórias devem ser realizadas em sua máquina local. Ao concluí-las, você terá preparado todas as configurações e os arquivos necessários para iniciar a implantação da sua aplicação:
  1. Crie dois arquivos compactados com a biblioteca ABBYY FineReader Engine e o Serviço de Licenciamento (por exemplo, LibraryPackage.zip e LSPackage.zip). Você pode criar a lista de arquivos automaticamente com o auxílio do arquivo FREngineDistribution.csv. Utilize o ABBYY FineReader Engine e o License Server do mesmo pacote; caso contrário, a compatibilidade não é garantida.
  2. Crie a conta do Azure Storage (frestorage, neste artigo). Todas as instruções necessárias estão disponíveis no site do Azure.
  3. Crie três blob containers dentro de frestorage:
  • fre-lib - para os arquivos do ABBYY FineReader Engine
  • fre-input - para os arquivos de entrada
  • fre-output - para os resultados do processamento
  1. Faça o upload de LibraryPackage.zip e LSPackage.zip para o container fre-lib da forma mais conveniente (usando .NET, PowerShell, script Python ou os aplicativos Azure Storage Explorer/Azure Portal).
Como exemplo, um projeto WorkerRole é utilizado para operar em um ambiente configurado. Todos os arquivos de configuração necessários (.csdef e Cloud.cscfg) são gerados automaticamente após a criação do projeto.

Implantando o ABBYY FineReader Engine no Cloud Service

Para implantar o ABBYY FineReader Engine no seu novo projeto WorkerRole:
  1. Especifique os parâmetros do Cloud.cscfg conforme desejado.
  2. Especifique os parâmetros do arquivo .csdef:
  • (opcional) as configurações do seu WorkerRole
  • (opcional) o tamanho da sua máquina virtual
  • (obrigatório) o armazenamento local da sua função (seção LocalStorage — neste artigo, o armazenamento é denominado LocalStorage1). Defina-o com pelo menos 3 GB para garantir que o pacote do ABBYY FineReader Engine seja implantado corretamente.
  • (obrigatório) a ordem de inicialização da sua função (utilize os exemplos de código e as configurações da seção de exemplos de código)
  1. Implemente seu projeto WorkerRole para processar arquivos do container fre-input e publicar os resultados do processamento no container fre-output:
  • o método OnStart para preparar o seu Cloud Service para o funcionamento. Esse método é usado para inicializar o Engine e configurar o protocolo TLS.
  • o método RunAsync para processar ciclicamente os arquivos obtidos do container fre-input. Esse método detecta os arquivos no container fre-input, os processa em memória e os coloca no container fre-output (consulte Processor.cs, IFileWriter.cs e Config.cs).
  • o método OnStop para encerrar o funcionamento do Cloud Service. Esse método é usado para desinicializar o Engine.

Exemplos de Código

Esta seção inclui exemplos de código usados para configurar a ordem de inicialização de funções e as configurações do ABBYY FineReader Engine:
  • CleanUpOnStart - exclui a versão anterior do ABBYY FineReader Engine e simplifica seu procedimento de atualização (consulte a listagem nos arquivos CleanUpOnStart.cmd e CleanUpOnStart.ps1 abaixo).
  • PreparePoShModules - baixa o SDK necessário para o PowerShell trabalhar com o Azure Storage (consulte a listagem nos arquivos PreparePoShModules.cmd e PreparePoShModules.ps1 abaixo).
  • PrepareLibrary - faz upload e descompacta a biblioteca ABBYY FineReader Engine do container fre-lib para o armazenamento local (consulte a listagem nos arquivos PrepareLibrary.cmd e PrepareLibrary.ps1 abaixo).
  • PrepareLS - faz upload, descompacta e inicia o Serviço de Licenciamento a partir do container fre-lib (consulte a listagem nos arquivos PrepareLS.cmd e PrepareLS.ps1 abaixo).
Os scripts listados acima devem ser executados exatamente na ordem indicada, antes de iniciar a aplicação. Para personalizar a ordem de execução dos scripts, especifique os seguintes atributos:
  • taskType=“simple” - as tarefas são executadas de forma síncrona, uma de cada vez.
  • executionContext=“elevated” - executa um script de inicialização com privilégios de administrador (necessário para instalar qualquer aplicação e executar o LicensingService.exe)
Como resultado, será criada uma pasta gerenciada pelo Cloud Service. Essa pasta é utilizada para fazer upload do ABBYY FineReader Engine pelos scripts listados acima e para carregar o FREngine.dll para sua posterior implementação no exemplo.
rem   Script para limpar pacotes anteriores do ABBYY FineReader Engine e do Licensing Service
echo Cleaning up before launching service >> ".\CleanUpOnStart.log" 2>&1
PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\CleanUpOnStart.ps1 >> ".\CleanUpOnStart.log" 2>&1
echo Cleaning up beafore launching service. >> ".\CleanUpOnStart.log" 2>&1
 
exit /B %errorlevel%
Write-Host "CleanUpOnStart.ps1 started...";
# caminho para o armazenamento local (consulte ServiceDefinition.csdef)
$local_storage_name = "LocalStorage1";
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.WindowsAzure.ServiceRuntime");
$local_storage_path = ([Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment]::GetLocalResource($local_storage_name)).RootPath.TrimEnd('\\');
# encerrando o processo do Serviço de Licenciamento, se estiver em execução
$processes = Get-Process;
foreach( $process in $processes ) {
    if( $process.Name -eq "LicesingService" ) {
        Stop-Process $process;
    }
}
 
# limpando e recriando a pasta fre_packages
$destination_path = Join-Path -Path $local_storage_path -ChildPath 'fre_packages';
if( Test-Path -Path $destination_path ) {
    Remove-Item $destination_path -Recurse -Force;
}
New-Item -ItemType Directory -Path $destination_path;
# limpando e recriando a pasta de entrada
$destination_path = Join-Path -Path $local_storage_path -ChildPath 'Input';
if( Test-Path -Path $destination_path ) {
    Remove-Item $destination_path -Recurse -Force;
}
New-Item -ItemType Directory -Path $destination_path;
# limpando e recriando a pasta de resultados
$destination_path = Join-Path -Path $local_storage_path -ChildPath 'Results';
if( Test-Path -Path $destination_path ) {
    Remove-Item $destination_path -Recurse -Force;
}
New-Item -ItemType Directory -Path $destination_path;
Write-Host ("A pasta de recursos foi limpa.");
rem   Script para preparar os módulos para conexão com a conta de armazenamento e download dos pacotes do ABBYY FineReader Engine e do Serviço de Licenciamento
 
echo Preparando Módulos PoSh >> ".\PreparePoShModules.log" 2>&1
PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\PreparePoShModules.ps1 >> ".\PreparePoShModules.log" 2>&1
echo Módulos PoSh preparados. >> ".\PreparePoShModules.log" 2>&1
 
exit /B %errorlevel%
# instalando módulos
Write-Host "PreparePoShModules.ps1 started...";
# instalando o provedor NuGet para download dos módulos do Azure
Write-Host "Installing NuGet package provider...";
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force;
 
# instalando o módulo Azure Storage para operação com blobs
Write-Host "Installing Az.Storage module...";
Install-Module -Name Az.Storage -Scope CurrentUser -Repository PSGallery -Force -AllowClobber;
Write-Host ("PoSh modules were prepared.");
rem   Script para preparar o pacote do ABBYY FineReader Engine
echo Preparando Biblioteca >> ".\PrepareLibrary.log" 2>&1
PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\PrepareLibrary.ps1 >> ".\PrepareLibrary.log" 2>&1
echo Biblioteca preparada. >> ".\PrepareLibrary.log" 2>&1
exit /B %errorlevel%
Write-Host "PrepareLibrary.ps1 started...";
# container com pacotes do ABBYY FineReader Engine e do Serviço de Licenciamento
$container_name = 'fre-lib';
# connecting string da conta de armazenamento
$connection_string = "<connection_string_to_frestorage>";
Write-Host "Configuration";
Write-Host ("Container name: " + $container_name);
Write-Host ("Connection string: " + $storage_account);
# conectando ao objeto de armazenamento
Write-Host ("Connecting to storage account...");
$storage_account = New-AzStorageContext -ConnectionString $connection_string;
# obtendo blobs (basicamente arquivos)
Write-Host ("Getting blobs from container...");
$fre_blobs = Get-AzStorageBlob -Container $container_name -Context $storage_account;
# caminho para o armazenamento local (consulte ServiceDefinition.csdef)
$local_storage_name = "LocalStorage1";
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.WindowsAzure.ServiceRuntime");
$local_storage_path = ([Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment]::GetLocalResource($local_storage_name)).RootPath.TrimEnd('\\');
$destination_path = Join-Path -Path $local_storage_path -ChildPath 'fre_packages';
# baixando apenas o blob necessário
Write-Host ("Downloading FRE package from container...");
foreach( $blob in $fre_blobs ) {
    Write-Host $blob.Name;
    if( $blob.Name -eq "LibraryPackage.zip" ) {
        Write-Host ("Downloading blob: " + $blob.Name + "to " + $destination_path);
        Get-AzStorageBlobContent -Container $container_name -Blob $blob.Name `
            -Destination (Join-Path -Path $destination_path -ChildPath $blob.Name) -Context $storage_account;
    } else {
        Write-Host ("Downloading blob " + $blob.Name + " was skipped.");
    }
}
# descompactando o pacote baixado (a biblioteca antiga será substituída forçosamente)
Write-Host ("Unzipping library package...");
Expand-Archive -Path (Join-Path -Path $destination_path -ChildPath "LibraryPackage.zip") `
    -DestinationPath (Join-Path -Path $destination_path -ChildPath "LibraryPackage") -Force;
Write-Host ("Library package was prepared.");
rem   Script para preparar o pacote do Serviço de Licenciamento e iniciar o LicensingService.exe
echo Preparing LS >> ".\PrepareLS.log" 2>&1
PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\PrepareLS.ps1 >> ".\PrepareLS.log" 2>&1
echo LS was prepared. >> ".\PrepareLS.log" 2>&1
exit /B %errorlevel%
Write-Host "PrepareLS.ps1 iniciado...";
# container com pacotes do ABBYY FineReader Engine e do Serviço de Licenciamento
$container_name = 'fre-lib';
# connecting string da conta de armazenamento
$connection_string = "<connection_string_to_frestorage>";
Write-Host "Configuração";
Write-Host ("Nome do container: " + $container_name);
Write-Host ("Connection string: " + $storage_account);
# conectando ao objeto de armazenamento
Write-Host ("Conectando à conta de armazenamento...");
$storage_account = New-AzStorageContext -ConnectionString $connection_string;
# obtendo blobs (basicamente arquivos)
Write-Host ("Obtendo blobs do container...");
$fre_blobs = Get-AzStorageBlob -Container $container_name -Context $storage_account;
# caminho para o armazenamento local (consulte ServiceDefinition.csdef)
$local_storage_name = "LocalStorage1";
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.WindowsAzure.ServiceRuntime");
$local_storage_path = ([Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment]::GetLocalResource($local_storage_name)).RootPath.TrimEnd('\\');
$destination_path = Join-Path -Path $local_storage_path -ChildPath 'fre_packages'; 
# baixando o pacote do Serviço de Licenciamento
Write-Host ("Baixando o pacote do Serviço de Licenciamento do container...");
foreach( $blob in $fre_blobs ) {
    Write-Host $blob.Name;
    if( $blob.Name -eq "LSPackage.zip" ) {
        Write-Host ("Baixando blob: " + $blob.Name + "para " + $destination_path);
        Get-AzStorageBlobContent -Container $container_name -Blob $blob.Name `
            -Destination (Join-Path -Path $destination_path -ChildPath $blob.Name) -Context $storage_account;
    } else {
        Write-Host ("Download do blob " + $blob.Name + " foi ignorado.");
    }
}
# descompactando o pacote baixado
Write-Host ("Descompactando o pacote da biblioteca...");
Expand-Archive -Path (Join-Path -Path $destination_path -ChildPath "LSPackage.zip") `
    -DestinationPath (Join-Path -Path $destination_path -ChildPath "LSPackage") -Force;
# criando pastas para licenças
$program_data_path = [System.Environment]::ExpandEnvironmentVariables("%programdata%");
$path = Join-Path -Path $program_data_path -ChildPath "ABBYY\SDK\12\Licenses";
New-Item -ItemType Directory -Path $path -Force;
# iniciando o Serviço de Licenciamento em modo autônomo
Write-Host ("Iniciando o Serviço de Licenciamento...");
 
$licensing_service_app = Join-Path -Path $destination_path -ChildPath "LSPackage\LicensingService.exe";
Start-Process -FilePath $licesing_service_app -ArgumentList "/standalone";
Write-Host ("O pacote LS foi preparado e o LS foi iniciado.");
namespace WorkerRole1.Engine
{
    public class FileWriter : FREngine.IFileWriter, IDisposable
    {
        public FileWriter(string _resultBlobName, string _fileExtension)
        {
            resultBlobName = _resultBlobName;
            fileExtension = _fileExtension;
        }
        public void Open(string fileName, ref int bufferSize)
        {
            stream = new MemoryStream();
        }
        public void Write(byte[] data)
        {
            stream.Write(data, 0, data.Length);
        }
        public void Close() 
        {
            // Criando conexão com um novo blob em <OutputContainerName>
            // O resultado do processamento será armazenado lá
            BlobClient resultBlobClient = new BlobClient(Config.ConnectionString, Config.OutputContainerName,
                resultBlobName + fileExtension);
            // Sobrescrever arquivo existente
            resultBlobClient.DeleteIfExists();
            // Definindo a posição como 0 para gravar o arquivo desde o início
            stream.Position = 0;
            resultBlobClient.Upload(stream);
 
            stream.Close();
        }
        public void Dispose()
        {
            // Fechando o stream de memória ao descartar para poder acessá-lo após a gravação dos dados
            stream.Close();
        }
        private string resultBlobName;
        private string fileExtension;
        private MemoryStream stream;
    }
}
namespace WorkerRole1
{
    public class WorkerRole : RoleEntryPoint
    {
        private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        private readonly ManualResetEvent runCompleteEvent = new ManualResetEvent(false);
        Processor processor = new Processor();
 
        public override void Run()
        {
            Trace.TraceInformation("WorkerRole1 está em execução");
 
            try
            {
                this.RunAsync(this.cancellationTokenSource.Token).Wait();
            }
            finally
            {
                this.runCompleteEvent.Set();
            }
        }
        public override bool OnStart()
        {
            // Define o número máximo de conexões simultâneas
            ServicePointManager.DefaultConnectionLimit = 12;
 
            // Define o TLS como 12 para permitir a conexão com a conta de armazenamento
            System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
 
            // Para informações sobre como lidar com alterações de configuração,
            // consulte o tópico do MSDN em https://go.microsoft.com/fwlink/?LinkId=166357
 
            bool result = base.OnStart();
            Trace.TraceInformation("WorkerRole1 foi iniciado");
 
            processor.LoadEngine();
            return result;
        }
 
        public override void OnStop()
        {
            processor.UnloadEngine();
            Trace.TraceInformation("WorkerRole1 está sendo interrompido");
            this.cancellationTokenSource.Cancel();
            this.runCompleteEvent.WaitOne();
            base.OnStop();
            Trace.TraceInformation("WorkerRole1 foi interrompido");
        }
 
        private async Task RunAsync(CancellationToken cancellationToken)
        {
            // TODO: Substitua o trecho a seguir pela sua própria lógica
            while (!cancellationToken.IsCancellationRequested)
            {
                Trace.TraceInformation("Processando");
 
                try
                {
                    // Cria o contêiner e retorna um objeto cliente do contêiner
                    BlobContainerClient inputContainerClient = new BlobContainerClient(Config.ConnectionString, Config.InputContainerName);
                    foreach (BlobItem blobItem in inputContainerClient.GetBlobs())
                    {
                        Trace.TraceInformation("\t" + blobItem.Name);
                        BlobClient blobClient = new BlobClient(Config.ConnectionString, Config.InputContainerName, blobItem.Name);
 
                        Trace.TraceInformation("\t Fazendo download do blob para a memória: " + blobItem.Name);
                        MemoryStream memoryStream = new MemoryStream();
                        blobClient.DownloadTo(memoryStream);
                        Trace.TraceInformation("\t Processando no FRE: " + blobItem.Name);
                        string resultBlobName = processor.ProcessImage(memoryStream, blobItem.Name);
                        Trace.TraceInformation("\t Nome do blob de resultado no contêiner de saída: " + resultBlobName);
                        Trace.TraceInformation("\t Excluindo do contêiner de entrada: " + blobItem.Name);
                        blobClient.Delete();
                    }
                }
                catch (Exception error)
                {
                    Trace.TraceInformation("erro: " + error.Message);
                }
 
                await Task.Delay(1000);
            }
        }
    }
}
namespace WorkerRole1
{
    class Processor : IDisposable
    {
        EngineLoader engineLoader = null;
        private void displayMessage(string text)
        {
            File.AppendAllText(".\\FRE.log", text + "\n");
            Trace.TraceInformation("\t" + text );
        }        
        private void loadProfile()
        {
            engineLoader.Engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
        }        
        private void setupFREngine()
        {
            displayMessage("Carregando perfil predefinido...");
            loadProfile();
        }
        public void LoadEngine()
        {
            try {
                if (engineLoader == null)
                {
                    // O mesmo EngineLoader do exemplo Hello
                    engineLoader = new EngineLoader();
                }
                setupFREngine();
            }
            catch (Exception error)
            {
                displayMessage("erro: " + error.Message);
            }
        }
        public void UnloadEngine()
        {
            try
            {
                if (engineLoader != null)
                {
                    engineLoader.Dispose();
                    engineLoader = null;
                }
            }
            catch (Exception error)
            {
                displayMessage("erro: " + error.Message);
            }
        }
 
        public string ProcessImage( MemoryStream inputMemoryStream, string inputBlobName )
        {
            FRDocument document = engineLoader.Engine.CreateFRDocument();
            string resultBlobName = "";
            try
            {
               document.PageFlushingPolicy = FREngine.PageFlushingPolicyEnum.PFP_KeepInMemory;
 
                // Adicionar arquivo de imagem ao documento
                displayMessage("Carregando imagem...");
                IntPtr handle = Marshal.AllocHGlobal(inputMemoryStream.GetBuffer().Length);
                Marshal.Copy(inputMemoryStream.GetBuffer(), 0, handle, inputMemoryStream.GetBuffer().Length);
                document.AddImageFileFromMemory(handle.ToInt64(), null, null);
 
                // Reconhecer o documento
                displayMessage("Reconhecendo...");
                document.Process(null);
 
                // Salvar resultados
                displayMessage("Salvando resultados...");
                // Salvar resultados em PDF usando o cenário 'balanced'
                // FREngine.PDFExportParams pdfParams = engineLoader.Engine.CreatePDFExportParams();
                // pdfParams.Scenario = FREngine.PDFExportScenarioEnum.PES_Balanced;
 
                WorkerRole1.Engine.FileWriter fileWriter = new WorkerRole1.Engine.FileWriter(inputBlobName, ".pdf");
                resultBlobName = inputBlobName + ".pdf";
                document.ExportToMemory(fileWriter, FREngine.FileExportFormatEnum.FEF_PDF, null);
 
            }
            catch (Exception error)
            {
                displayMessage("erro: " + error.Message);
            }
            finally
            {
                // Fechar o documento
                document.Close();
            }
 
            return resultBlobName;
        }
 
        public void Dispose()
        {
            UnloadEngine();
        }
    }
}
class Config
{
    // Armazenamento local conforme definido em ServiceDefinition.csdef
    private static string LocalStorageName = "LocalStorage1";
    // String de conexão com o blob container
    public static readonly string ConnectionString = "<connection_string_to_frestorage>";
    // Nome dos containers de entrada e saída no seu armazenamento
    public static readonly string InputContainerName = "fre-input";
    public static readonly string OutputContainerName = "fre-output";
 
    // Retornar o ID do projeto do cliente para o ABBYY FineReader Engine
    public static String GetCustomerProjectId()
    {
        return "<Your_Customer_Project_ID>";
    }
 
    // Retornar o nome do token de licença
    public static String GetLicenseTokenName()
    {
        return "<Token_number>.ABBYY.ActivationToken";
    }
 
    // Retornar a senha da licença
    public static String GetLicensePassword()
    {
        return "<Your_Online_License_token_password>";
    }
    // Retornar o caminho do engine
    public static String GetEngineFolder()
    {
        string engineSubfolder = "fre_packages\\LibraryPackage\\Bin64";
        string engineDllFolder = Path.Combine(RoleEnvironment.GetLocalResource(LocalStorageName).RootPath, engineSubfolder);
        return engineDllFolder;
    }
}