Ausführen von ABBYY FineReader Engine in Azure-Diensten
Ausführen in Azure Cloud Service
Dieser Abschnitt enthält Anweisungen zum Bereitstellen einer FRE 12 for Windows-Anwendung in Azure Cloud Service. Als Beispiel dient ein Worker, der Dateien in einem Container eines Azure Speicherkontos verarbeitet.Zur Implementierung dieses Szenarios wird eine Online-Lizenz verwendet, die eine Verbindung zum Lizenzserver unter *.abbyy.com herstellt.
ABBYY Licensing Service kann jeweils nur eine Online-Lizenz gleichzeitig verwenden.
Das Bereitstellen Ihrer Anwendung in Cloud Service umfasst mehrere Schritte:
Einrichten Ihrer Online-Lizenz, Ihres lokalen Computers und Ihrer Cloud-Instanz anhand der Voraussetzungen
das Kennwort für die Token-Datei der Online-Lizenz
Für die Verwendung einer Online-Lizenz müssen an allen Orten, an denen der ABBYY Licensing Service installiert ist, die folgenden Bedingungen erfüllt sein:
Aktive Internetverbindung
Erlaubte Verbindungen zu *.abbyy.com über Port 443 (HTTPS)
GoDaddy-Root-Zertifikat für die Zertifizierungsstelle (Es muss im Zertifikatsspeicher „Vertrauenswürdige Stammzertifizierungsstellen“ des lokalen Computers installiert sein. Ausführliche Informationen zum Zertifikat finden Sie auf der GoDaddy-Website.)
Bevor Sie Ihren Cloud Service erstellen, verwenden Sie die folgende Spezifikation, um Ihren lokalen Rechner vorzubereiten:
Visual Studio 2019 und die zugehörigen Module für die Entwicklung von Anwendungen für Azure (siehe Azure-Features in Visual Studio oder verwenden Sie das Visual Studio-Installationsprogramm, um diese Module herunterzuladen)
Cloud Service-Emulatoren und Speicher für einfaches Debugging (weitere Informationen hier; Download über das Visual Studio-Installationsprogramm)
Vorlagenlösung für Azure Cloud Service (Extended Preview) mit einem einzelnen Worker-Rollenprojekt (weitere Informationen hier)
NuGet-Paket für die Arbeit mit Azure Storage und Blob-Containern – Azure.Storage.Blobs (Download hier)
ABBYY FineReader Engine-Wrapper für .NET Framework 4.7 (im Ordner C:\ProgramData\ABBYY\SDK\12\FineReader Engine\Inc.NET interops nach der Developer-Installation)
Für die Arbeit mit Blob-Containern überschriebene IFileWriter-Schnittstelle (siehe das Beispiel unten)
Die Cloud Service-Instanz dient zum Speichern Ihres WorkerRole-Projekts in Azure. Verwenden Sie die folgende Spezifikation, um diese Instanz einzurichten:
.NET Framework-Version für Ihren Service
PowerShell zum Bereitstellen von ABBYY FineReader Engine
NuGet 2.8.5.201 oder höher zum Hochladen des Azure SDK
Azure SDK für die Arbeit mit einem Azure Speicherkonto mithilfe von PowerShell
Die Vorbereitungsschritte sind auf Ihrem lokalen Rechner durchzuführen. Wenn Sie diese Schritte abschließen, bereiten Sie alle erforderlichen Einstellungen und Dateien vor, um mit der Bereitstellung Ihrer Anwendung zu beginnen:
Erstellen Sie zwei Archive mit der ABBYY FineReader Engine-Bibliothek und dem Licensing Service (zum Beispiel LibraryPackage.zip und LSPackage.zip). Sie können die Dateiliste mithilfe der Datei FREngineDistribution.csv automatisch erstellen. Verwenden Sie ABBYY FineReader Engine und License Server aus demselben Paket; andernfalls ist die Kompatibilität nicht gewährleistet.
Erstellen Sie das Azure Speicherkonto (in diesem Artikel frestorage). Alle erforderlichen Anleitungen finden Sie auf der Azure-Website.
Erstellen Sie drei Blob-Container in frestorage:
fre-lib - für die Dateien von ABBYY FineReader Engine
fre-input - für eingehende Dateien
fre-output - für die Verarbeitungsergebnisse
Laden Sie LibraryPackage.zip und LSPackage.zip auf die für Sie bequemste Weise in den Container fre-lib hoch (mit .NET, PowerShell, einem Python-Skript oder den Anwendungen Azure Storage Explorer/Azure Portal).
Als Beispiel wird ein WorkerRole-Projekt für die Ausführung in einer konfigurierten Umgebung verwendet. Alle erforderlichen Konfigurationsdateien (.csdef und Cloud.cscfg) werden nach dem Erstellen Ihres Projekts automatisch generiert.
Bereitstellen von ABBYY FineReader Engine in Cloud Service
So stellen Sie ABBYY FineReader Engine in Ihrem neuen WorkerRole-Projekt bereit:
Geben Sie die Parameter in Cloud.cscfg wie gewünscht an.
Geben Sie die Parameter der Datei .csdef an:
(optional) Ihre WorkerRole-Einstellungen
(optional) die Größe Ihrer virtuellen Maschine
(erforderlich) den lokalen Speicher Ihrer Rolle (Abschnitt LocalStorage – in diesem Artikel der Speicher mit dem Namen LocalStorage1). Legen Sie ihn auf mindestens 3 GB fest, damit das ABBYY FineReader Engine-Paket ordnungsgemäß bereitgestellt werden kann.
(erforderlich) die Startreihenfolge Ihrer Rolle (verwenden Sie die Codebeispiele und Einstellungen aus dem Abschnitt Codebeispiel)
Implementieren Sie Ihr WorkerRole-Projekt so, dass es Dateien aus dem Container fre-input verarbeitet und die Verarbeitungsergebnisse im Container fre-output ablegt:
die Methode OnStart, um Ihren Cloud Service für den Betrieb vorzubereiten. Diese Methode wird verwendet, um die Engine zu initialisieren und das TLS-Protokoll einzurichten.
die Methode RunAsync zur zyklischen Verarbeitung der Dateien aus dem Container fre-input. Diese Methode erkennt die Dateien im Container fre-input, verarbeitet sie im Speicher und legt sie im Container fre-output ab (siehe Processor.cs, IFileWriter.cs und Config.cs).
die Methode OnStop, um den Betrieb des Cloud Service zu beenden. Diese Methode wird verwendet, um die Engine zu deinitialisieren.
Dieser Abschnitt enthält Codebeispiele zum Konfigurieren der Startreihenfolge von Rollen und der Einstellungen von ABBYY FineReader Engine:
CleanUpOnStart - löscht die vorherige Version von ABBYY FineReader Engine und vereinfacht deren Aktualisierung (siehe die Auflistung unten in den Dateien CleanUpOnStart.cmd und CleanUpOnStart.ps1).
PreparePoShModules - lädt das SDK herunter, das benötigt wird, damit PowerShell mit Azure Storage arbeiten kann (siehe die Auflistung unten in den Dateien PreparePoShModules.cmd und PreparePoShModules.ps1).
PrepareLibrary - lädt die ABBYY FineReader Engine-Bibliothek aus dem Container fre-lib in den lokalen Speicher und entpackt sie dort (siehe die Auflistung unten in den Dateien PrepareLibrary.cmd und PrepareLibrary.ps1).
PrepareLS - lädt den Licensing Service aus dem Container fre-lib, entpackt ihn und startet ihn (siehe die Auflistung unten in den Dateien PrepareLS.cmd und PrepareLS.ps1).
Die oben aufgeführten Skripte müssen vor dem Starten Ihrer Anwendung genau in der angegebenen Reihenfolge ausgeführt werden. Um die Ausführungsreihenfolge der Skripte festzulegen, geben Sie die folgenden Attribute an:
taskType=“simple” - Aufgaben werden synchron nacheinander ausgeführt.
executionContext=“elevated” - Ausführen eines Startskripts mit Administratorrechten (erforderlich, um Anwendungen installieren und LicensingService.exe ausführen zu können)
Dadurch wird ein Ordner erstellt, den Cloud Service verwalten kann. Dieser Ordner wird verwendet, um ABBYY FineReader Engine mithilfe der oben aufgeführten Skripte hochzuladen und die FREngine.dll zur weiteren Verwendung im Beispiel zu laden.
CleanUpOnStart.cmd
rem Skript zum Bereinigen vorheriger Pakete von ABBYY FineReader Engine und 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...";# Pfad zum lokalen Speicher (siehe 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('\\');# Licensing Service-Prozess beenden, falls er ausgeführt wird$processes = Get-Process;foreach( $process in $processes ) { if( $process.Name -eq "LicesingService" ) { Stop-Process $process; }}# den Ordner fre_packages bereinigen und neu erstellen$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;# den Eingabeordner bereinigen und neu erstellen$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;# den Ordner Results bereinigen und neu erstellen$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 ("Resource folder was cleaned up.");
PreparePoShModules.cmd
rem Skript zum Vorbereiten von Modulen, um eine Verbindung mit dem Speicherkonto herzustellen und ABBYY FineReader Engine- und Licensing Service-Pakete herunterzuladenecho Preparing PoSh Modules >> ".\PreparePoShModules.log" 2>&1PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\PreparePoShModules.ps1 >> ".\PreparePoShModules.log" 2>&1echo PoSh Modules wer prepared. >> ".\PreparePoShModules.log" 2>&1exit /B %errorlevel%
PreparePoShModules.ps1
# Module installierenWrite-Host "PreparePoShModules.ps1 started...";# NuGet-Paketanbieter zum Herunterladen von Azure-Modulen installierenWrite-Host "Installing NuGet package provider...";Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force;# Az.Storage-Modul für die Arbeit mit Blob Storage installierenWrite-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 Skript zum Vorbereiten des ABBYY FineReader Engine-Paketsecho Preparing Library >> ".\PrepareLibrary.log" 2>&1PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\PrepareLibrary.ps1 >> ".\PrepareLibrary.log" 2>&1echo Library was prepared. >> ".\PrepareLibrary.log" 2>&1exit /B %errorlevel%
PrepareLibrary.ps1
Write-Host "PrepareLibrary.ps1 gestartet...";# Container mit Paketen für ABBYY FineReader Engine und Licensing Service$container_name = 'fre-lib';# Verbindungszeichenfolge für das Speicherkonto $connection_string = "<connection_string_to_frestorage>";Write-Host "Konfiguration";Write-Host ("Containername: " + $container_name);Write-Host ("Verbindungszeichenfolge: " + $storage_account);# Verbindung mit dem Speicherkonto herstellenWrite-Host ("Verbindung mit dem Speicherkonto wird hergestellt...");$storage_account = New-AzStorageContext -ConnectionString $connection_string;# Blobs abrufen (im Wesentlichen Dateien)Write-Host ("Blobs aus dem Container werden abgerufen...");$fre_blobs = Get-AzStorageBlob -Container $container_name -Context $storage_account;# Pfad zum lokalen Speicher (siehe 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';# nur den benötigten Blob herunterladenWrite-Host ("FRE-Paket aus dem Container wird heruntergeladen...");foreach( $blob in $fre_blobs ) { Write-Host $blob.Name; if( $blob.Name -eq "LibraryPackage.zip" ) { Write-Host ("Blob wird heruntergeladen: " + $blob.Name + " nach " + $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 ("Blob " + $blob.Name + " wird nicht heruntergeladen."); }}# heruntergeladenes Paket entpacken (die vorhandene Bibliothek wird dabei zwangsweise überschrieben)Write-Host ("Bibliothekspaket wird entpackt...");Expand-Archive -Path (Join-Path -Path $destination_path -ChildPath "LibraryPackage.zip") ` -DestinationPath (Join-Path -Path $destination_path -ChildPath "LibraryPackage") -Force;Write-Host ("Bibliothekspaket wurde vorbereitet.");
PrepareLS.cmd
rem Skript zum Vorbereiten des Licensing Service-Pakets und zum Starten von LicensingService.exeecho LS wird vorbereitet >> ".\PrepareLS.log" 2>&1PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\PrepareLS.ps1 >> ".\PrepareLS.log" 2>&1echo LS wurde vorbereitet. >> ".\PrepareLS.log" 2>&1exit /B %errorlevel%
PrepareLS.ps1
Write-Host "PrepareLS.ps1 wurde gestartet...";# Container mit Paketen für ABBYY FineReader Engine und Licensing Service$container_name = 'fre-lib';# Verbindungszeichenfolge für das Speicherkonto$connection_string = "<connection_string_to_frestorage>";Write-Host "Konfiguration";Write-Host ("Containername: " + $container_name);Write-Host ("Verbindungszeichenfolge: " + $storage_account);# Verbindung mit dem Speicherobjekt herstellenWrite-Host ("Verbindung mit dem Speicherkonto wird hergestellt...");$storage_account = New-AzStorageContext -ConnectionString $connection_string;# Blobs abrufen (im Grunde Dateien)Write-Host ("Blobs aus dem Container werden abgerufen...");$fre_blobs = Get-AzStorageBlob -Container $container_name -Context $storage_account;# Pfad zum lokalen Speicher (siehe 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'; # Licensing Service-Paket herunterladenWrite-Host ("Licensing Service-Paket wird aus dem Container heruntergeladen...");foreach( $blob in $fre_blobs ) { Write-Host $blob.Name; if( $blob.Name -eq "LSPackage.zip" ) { Write-Host ("Blob wird heruntergeladen: " + $blob.Name + " nach " + $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 ("Herunterladen von Blob " + $blob.Name + " wurde übersprungen."); }}# Heruntergeladenes Paket entpackenWrite-Host ("Bibliothekspaket wird entpackt...");Expand-Archive -Path (Join-Path -Path $destination_path -ChildPath "LSPackage.zip") ` -DestinationPath (Join-Path -Path $destination_path -ChildPath "LSPackage") -Force;# Ordner für Lizenzen erstellen$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;# Licensing Service im Standalone-Modus startenWrite-Host ("Licensing Service wird gestartet...");$licensing_service_app = Join-Path -Path $destination_path -ChildPath "LSPackage\LicensingService.exe";Start-Process -FilePath $licesing_service_app -ArgumentList "/standalone";Write-Host ("LS-Paket wurde vorbereitet und LS wurde gestartet.");
Implementierung der IFileWriter-Schnittstelle
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() { // Verbindung mit einem neuen Blob in <OutputContainerName> herstellen // Das Verarbeitungsergebnis wird dort gespeichert BlobClient resultBlobClient = new BlobClient(Config.ConnectionString, Config.OutputContainerName, resultBlobName + fileExtension); // Vorhandene Datei überschreiben resultBlobClient.DeleteIfExists(); // Position auf 0 setzen, damit die Datei von Anfang an geschrieben wird stream.Position = 0; resultBlobClient.Upload(stream); stream.Close(); } public void Dispose() { // MemoryStream beim Freigeben schließen, damit nach dem Schreiben der Daten darauf zugegriffen werden kann stream.Close(); } private string resultBlobName; private string fileExtension; private MemoryStream stream; }}
Beispiel für die Verarbeitung von Dateien mit 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 is running"); try { this.RunAsync(this.cancellationTokenSource.Token).Wait(); } finally { this.runCompleteEvent.Set(); } } public override bool OnStart() { // Legen Sie die maximale Anzahl gleichzeitiger Verbindungen fest ServicePointManager.DefaultConnectionLimit = 12; // Setzen Sie TLS auf 12, damit eine Verbindung mit dem Speicherkonto hergestellt werden kann System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12; // Informationen zum Umgang mit Konfigurationsänderungen // finden Sie im entsprechenden MSDN-Thema unter https://go.microsoft.com/fwlink/?LinkId=166357 bool result = base.OnStart(); Trace.TraceInformation("WorkerRole1 has been started"); processor.LoadEngine(); return result; } public override void OnStop() { processor.UnloadEngine(); Trace.TraceInformation("WorkerRole1 is stopping"); this.cancellationTokenSource.Cancel(); this.runCompleteEvent.WaitOne(); base.OnStop(); Trace.TraceInformation("WorkerRole1 has stopped"); } private async Task RunAsync(CancellationToken cancellationToken) { // TODO: Ersetzen Sie den folgenden Code durch Ihre eigene Logik while (!cancellationToken.IsCancellationRequested) { Trace.TraceInformation("Working"); try { // Erstellen Sie den Container und geben Sie ein Container-Clientobjekt zurück 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 Downloading blob to memory: " + blobItem.Name); MemoryStream memoryStream = new MemoryStream(); blobClient.DownloadTo(memoryStream); Trace.TraceInformation("\t Processing in FRE: " + blobItem.Name); string resultBlobName = processor.ProcessImage(memoryStream, blobItem.Name); Trace.TraceInformation("\t Result blob name in output container: " + resultBlobName); Trace.TraceInformation("\t Deleting from input container: " + blobItem.Name); blobClient.Delete(); } } catch (Exception error) { Trace.TraceInformation("error: " + error.Message); } await Task.Delay(1000); } } }}