Executando o ABBYY FineReader Engine nos Serviços do Azure
Executando no Azure Cloud Service
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:
Organizar sua Licença Online, máquina local e instância na nuvem usando os pré-requisitos
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).
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)
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)
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:
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.
Crie a conta do Azure Storage (frestorage, neste artigo). Todas as instruções necessárias estão disponíveis no site do Azure.
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
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:
Especifique os parâmetros do Cloud.cscfg conforme desejado.
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)
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.
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.
CleanUpOnStart.cmd
rem Script para limpar pacotes anteriores do ABBYY FineReader Engine e do Licensing Serviceecho Cleaning up before launching service >> ".\CleanUpOnStart.log" 2>&1PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\CleanUpOnStart.ps1 >> ".\CleanUpOnStart.log" 2>&1echo Cleaning up beafore launching service. >> ".\CleanUpOnStart.log" 2>&1exit /B %errorlevel%
CleanUpOnStart.ps1
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.");
PreparePoShModules.cmd
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 Licenciamentoecho Preparando Módulos PoSh >> ".\PreparePoShModules.log" 2>&1PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\PreparePoShModules.ps1 >> ".\PreparePoShModules.log" 2>&1echo Módulos PoSh preparados. >> ".\PreparePoShModules.log" 2>&1exit /B %errorlevel%
PreparePoShModules.ps1
# instalando módulosWrite-Host "PreparePoShModules.ps1 started...";# instalando o provedor NuGet para download dos módulos do AzureWrite-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 blobsWrite-Host "Installing Az.Storage module...";Install-Module -Name Az.Storage -Scope CurrentUser -Repository PSGallery -Force -AllowClobber;Write-Host ("PoSh modules were prepared.");
PrepareLibrary.cmd
rem Script para preparar o pacote do ABBYY FineReader Engineecho Preparando Biblioteca >> ".\PrepareLibrary.log" 2>&1PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\PrepareLibrary.ps1 >> ".\PrepareLibrary.log" 2>&1echo Biblioteca preparada. >> ".\PrepareLibrary.log" 2>&1exit /B %errorlevel%
PrepareLibrary.ps1
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 armazenamentoWrite-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árioWrite-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.");
PrepareLS.cmd
rem Script para preparar o pacote do Serviço de Licenciamento e iniciar o LicensingService.exeecho Preparing LS >> ".\PrepareLS.log" 2>&1PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\PrepareLS.ps1 >> ".\PrepareLS.log" 2>&1echo LS was prepared. >> ".\PrepareLS.log" 2>&1exit /B %errorlevel%
PrepareLS.ps1
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 armazenamentoWrite-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 LicenciamentoWrite-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 baixadoWrite-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ônomoWrite-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.");
Implementação da Interface IFileWriter
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; }}
Exemplo de processamento de arquivos usando ABBYY FineReader Engine
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); } } }}
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; }}