Stellen Sie ABBYY FineReader Engine 12 for Windows mit einem Paar von WebJob-Projekten und Azure Storage-Blob-Containern für die Dateiverarbeitung in Azure App Service bereit.
Use this file to discover all available pages before exploring further.
In diesem Abschnitt wird beschrieben, wie Sie eine FRE 12 for Windows-Anwendung in Azure App Service bereitstellen. Als Beispiel dient ein Paar von WebJob-Projekten, die Daten aus einem Azure Storage-Konto verwenden. Die Dateiverarbeitung erfolgt mithilfe eines Blob-Containers. Mit diesem Szenario erzielen Sie die besten Erkennungsergebnisse für kleine einseitige Dokumente wie Rechnungen, Kassenbelege usw.Das Bereitstellen Ihrer Anwendung in App Service umfasst mehrere Schritte:
Einrichten Ihres lokalen Computers und Ihrer App-Instanz anhand der Voraussetzungen
Bevor Sie Ihren App Service erstellen, verwenden Sie die folgende Spezifikation, um Ihren lokalen Rechner entsprechend einzurichten:
Visual Studio 2019 und die Module für die Entwicklung von Anwendungen für Azure (siehe Azure feature in Visual Studio oder verwenden Sie den Visual Studio Installer, um diese Module herunterzuladen)
ABBYY FineReader Engine-Wrapper für .NET Framework 4.7 (im Ordner C:\ProgramData\ABBYY\SDK\12\FineReader Engine\Inc.NET interops nach der Installation der Entwicklerversion)
Überschriebene IFileWriter-Schnittstelle für die Arbeit mit Blob-Containern (siehe das Beispiel unten)
Azure Storage Explorer (optional – hier herunterladen)
Virtuelle Maschine in Ihrem Azure-Konto für die Lizenzierung. Für die weitere Konfiguration benötigen Sie:
IP-Adresse
Offenen Verbindungsport (Standard oder benutzerdefiniert). Zum Öffnen verwenden Sie eine Firewall
Verbindungsdetails für das Sockets-Netzwerkprotokoll
Die vorbereitenden Schritte sind auf Ihrem lokalen Rechner durchzuführen. Nach Abschluss dieser Schritte haben Sie alle erforderlichen Einstellungen und Dateien vorbereitet, um die Bereitstellung Ihrer Anwendung zu starten:
Erstellen Sie ein Archiv mit der ABBYY FineReader Engine-Bibliothek (z. B. LibraryPackage.zip). Die Liste der Dateien ist in der Datei FREngineDistribution.csv aufgeführt. Wichtig! Wenn Sie über begrenzten Speicherplatz verfügen (z. B. bei Verwendung eines App Service Plans mit 1 GB Speicherplatz), empfehlen wir die Verwendung der Option /extract, um ein benutzerdefiniertes ABBYY FineReader Engine-Paket mit minimaler Größe zu erstellen. Der verbleibende Speicherplatz wird für die Verarbeitung der Dateien genutzt. Beachten Sie beim Erstellen des Archivs, dass die ABBYY FineReader Engine-Lizenzierungseinstellungen entsprechend den Einstellungen der virtuellen Maschine konfiguriert sein müssen:
Das Netzwerkprotokoll Sockets muss verwendet werden.
Die Online License-Token-Datei muss sich im Ordner Bin64 befinden.
Erstellen Sie ein Azure Storage-Konto (in diesem Artikel als frestorage bezeichnet). Alle erforderlichen Anweisungen finden Sie auf der Azure-Website.
Erstellen Sie Ihren App Service nach Bedarf (Anweisungen finden Sie hier).
Erstellen Sie zwei Blob-Container in frestorage:
fre-lib – für die ABBYY FineReader Engine-Dateien
processing-container – für Verarbeitungsergebnisse
Laden Sie LibraryPackage.zip auf dem für Sie bequemsten Weg in den Container fre-lib hoch (mithilfe von .NET, PowerShell, Python-Skript oder den Anwendungen Azure Storage Explorer/Azure Portal).
Stellen Sie die virtuelle Maschine mit den Lizenzierungseinstellungen in Ihrem Azure-Konto bereit und konfigurieren Sie sie:
Installieren Sie das License Manager Utility über installLM.exe aus der LibraryPackage.zip.
Richten Sie das Netzwerkprotokoll Sockets in der LicensingSettings.xml ein und starten Sie anschließend den Licensing Service neu.
Stellen Sie sicher, dass Azure App Service auf den Verbindungsport des Licensing Service zugreifen kann (passen Sie dazu die Windows-Firewallregeln auf der virtuellen Maschine an).
Aktivieren Sie Ihre Lizenz (nur bei Software-Schutz erforderlich; Online-Schutz erfordert keine Aktivierung).
Erstellen Sie zwei Queues in frestorage:
processing-queue – zum Festlegen der Dateiverarbeitungsaufgaben
status-queue – zur Benachrichtigung über den Abschluss von Aufgaben
Erstellen Sie zwei Azure WebJob-Projekte (.NET Framework) in Visual Studio 2019 für die Arbeit mit frestorage:
FreDeployerJob – zum Bereitstellen von LibraryPackage.zip in App Service (siehe Auflistung der zugehörigen Dateien: Config.cs, Functions.cs, Program.cs unten)
FreProcessorJob – für die Dokumentverarbeitung (siehe Auflistung der zugehörigen Dateien: Config.cs, Functions.cs, Program.cs, EngineLoader.cs, IFileWriter.cs, Processor.cs unten)
Bereitstellen und Ausführen von ABBYY FineReader Engine in App Service
So stellen Sie ABBYY FineReader Engine bereit:
Veröffentlichen Sie FreDeployerJob mit Visual Studio in Azure App Service (legen Sie als WebJob Type Triggered fest).
Öffnen Sie Ihren App Service im Azure-Portal.
Öffnen Sie die WebJobs Ihres App Service.
Suchen Sie FreDeployerJob in der Liste der WebJobs.
Starten Sie FreDeployerJob auf der Registerkarte WebJobs mit dem Befehl „Run“ im Kontextmenü (Rechtsklick).
Sie können die Registerkarte Logs öffnen, um das Ergebnis der Bereitstellung zu überprüfen. Wenn die Bereitstellung erfolgreich ist, wird LibraryPackage.zip aus dem Container fre-lib hochgeladen und im Ordner %HOME_EXPANDED% bereitgestellt, der in App Service für alle Komponenten verfügbar ist.Um FreProcessorJob bereitzustellen, veröffentlichen Sie FreProcessorJob mit Visual Studio in Azure App Service (legen Sie als WebJob Type Continuous fest). Danach wird FreProcessorJob in der WebJobs-Liste Ihres App Service angezeigt.So verarbeiten Sie eine Datei:
Laden Sie die Datei, die Sie verarbeiten möchten, in den processing-container hoch.
Fügen Sie der processing-queue eine JSON-Nachricht für eine neue Verarbeitungsaufgabe im Format {“blob-item-name” : “file_name”} hinzu. Wenn Sie Demo.tif in den processing-container hochladen, sollte Ihre Nachricht wie folgt lauten:
{"blob-item-name" : "Demo.tif"}
Warten Sie, bis die Aufgabe abgeschlossen ist. Sobald die neue Aufgabe eingerichtet ist, beginnt FreProcessorJob, die angegebene Datei im Speicher zu verarbeiten. Die Statuswarteschlange enthält Einträge zur Ausführung dieser Aufgabe.
Suchen Sie die Ausgabedatei im Verarbeitungscontainer.
FreProcessorJob arbeitet als Single-Thread-Prozess. Wenn Sie Ihre Dateien parallel verarbeiten möchten, müssen Sie mehrere FreProcessorJob erstellen, die dieselbe Warteschlange überwachen. 2. Jeder zusätzliche FreProcessorJob benötigt zusätzlichen Speicher. Berücksichtigen Sie dies bei der Auswahl Ihres Service Plans. Im Azure Free Service Plan ist es beispielsweise sinnvoll, nur einen FreProcessorJob zu verwenden, der wenig Speicher benötigt und dadurch eine stabile Dateiverarbeitung gewährleistet. 3. Ein einzelner FreProcessorJob eignet sich nicht für die Verarbeitung großer mehrseitiger Dokumente. Ziehen Sie in diesem Fall stattdessen die Erkennung Ihres Dokuments in Azure Cloud Service oder auf einer Azure Virtual Machine in Betracht.
using System.IO;class Config{ // Verbindungszeichenfolge zum Blob-Container public static readonly string ConnectionString = "your_connection_string"; // Das Verzeichnis HOME_EXPANDED ist für alle WebJobs-Ordner gemeinsam public static readonly string LibraryFolder = Path.Combine(System.Environment.GetEnvironmentVariable("HOME_EXPANDED"), "FRE"); // Namen der Eingabe- und Ausgabe-Container in Ihrem Storage public static readonly string LibraryContainerName = "fre-lib";
Functions.cs
namespace FreDeployerJob{ public class Functions { // Diese Funktion wird nicht automatisch ausgelöst – Sie müssen sie manuell starten [NoAutomaticTrigger] [Timeout("01:00:00")] public static void DeployFRE() { Console.WriteLine("Deploying FRE"); // Verbindung mit dem vorhandenen Eingabe-Container <InputContainerName> über die Verbindungszeichenfolge des Storage-Kontos herstellen BlobContainerClient inputContainerClient = new BlobContainerClient(Config.ConnectionString, Config.LibraryContainerName); // Bibliotheksverzeichnis sowie AppData- und Temp-Ordner für die Initialisierung von ABBYY FineReader Engine erstellen if (Directory.Exists(Config.LibraryFolder) == true) { Directory.Delete(Config.LibraryFolder, true); } Directory.CreateDirectory(Config.LibraryFolder); Directory.CreateDirectory(Path.Combine(Config.LibraryFolder, "Temp")); Directory.CreateDirectory(Path.Combine(Config.LibraryFolder, "AppData")); // Die Blobs im Container durchlaufen. Ein Blob in <InputContainerName> entspricht einer Bilddatei foreach (BlobItem blobItem in inputContainerClient.GetBlobs()) { Console.WriteLine("\t" + blobItem.Name); // Nach der Light-Version der ABBYY FineReader Engine-Bibliothek suchen if (blobItem.Name == "LibraryPackage.zip") { Console.WriteLine("LibraryPackage.zip was found."); // Verbindung mit dem Blob herstellen, um auf dessen Inhalt zuzugreifen BlobClient blobClient = new BlobClient(Config.ConnectionString, Config.LibraryContainerName, blobItem.Name); Console.WriteLine("Downloading to memory..."); // ZIP-Datei in den Speicher herunterladen using (MemoryStream memoryStream = new MemoryStream()) { blobClient.DownloadTo(memoryStream); Console.WriteLine("LibraryPackage.zip was downloaded."); Console.WriteLine("Unzipping..."); // Ohne Verwendung eines temporären Ordners entpacken (nur Verarbeitung im Speicher) using (ZipArchive archive = new ZipArchive(memoryStream)) { foreach (ZipArchiveEntry entry in archive.Entries) { string subDirectory = Path.GetDirectoryName(Path.Combine(Config.LibraryFolder, entry.FullName)); if (Directory.Exists(subDirectory) == false) { Directory.CreateDirectory(subDirectory); } if (entry.Name.Length != 0) { entry.ExtractToFile(Path.Combine(subDirectory, entry.Name)); } } } Console.WriteLine("LibraryPackage.zip was unzipped to HOME_EXPANDED."); } } } } }}
Program.cs
namespace FreDeployerJob{ // Weitere Informationen zum Microsoft Azure WebJobs SDK finden Sie unter https://go.microsoft.com/fwlink/?LinkID=320976 class Program { // Bitte legen Sie die folgenden Verbindungszeichenfolgen in app.config fest, damit diese WebJobs ausgeführt werden können: // AzureWebJobsDashboard und AzureWebJobsStorage static void Main() { // Beim Auslösen ruft dieser WebJob nur diese Methode auf Functions.DeployFRE(); } }}
using System;using System.IO;namespace FreProcessorJob{ class Config { // Verbindungszeichenfolge für den Blob-Container public static readonly string ConnectionString = "your_connection_string"; // Das Verzeichnis HOME_EXPANDED wird von allen WebJobs-Ordnern gemeinsam verwendet // Es ist dasselbe wie im Projekt FreDeployerJob public static readonly string LibraryFolder = Path.Combine(System.Environment.GetEnvironmentVariable("HOME_EXPANDED"), "FRE"); // Name des Verarbeitungs-Containers in Ihrem Speicher public static readonly string ProcessingContainerName = "processing-container"; // Name der Verarbeitungswarteschlange public static readonly string ProcessingQueueName = "processing-queue"; public static readonly string StatusQueueName = "status-queue"; // Gibt die Customer Project ID für ABBYY FineReader Engine zurück public static String GetCustomerProjectId() { return "your_cpid"; } // Gibt den Namen des Online-Lizenz-Tokens zurück // Wenn Sie keine Online-Lizenz verwenden, lassen Sie den String leer // Das Token sollte sich im Ordner Bin64 des Bibliothekspakets befinden public static String GetLicenseTokenName() { return "your_online_license_token_if_you_have_it"; } // Gibt das Kennwort der Online-Lizenz zurück // Wenn Sie keine Online-Lizenz verwenden, lassen Sie den String leer public static String GetLicensePassword() { return "online_license_password_if_you have_it"; } // Gibt den Namen des Lizenz-Tokens zurück // Die Lizenzierung befindet sich im Ordner Bin64 des Bibliothekspakets public static String GetLicenseTokenName() { return "your_licence_for_ABBYY FineReader Engine"; } // Gibt das Lizenzkennwort zurück public static String GetLicensePassword() { return "license_password"; } // Gibt den Engine-Pfad zurück public static String GetEngineFolder() { string engineSubfolder = "Bin64"; string engineDllFolder = Path.Combine(LibraryFolder, engineSubfolder); return engineDllFolder; } }}
Functions.cs
using Microsoft.Azure.WebJobs;using System;using System.IO;using Azure.Storage.Blobs;using Azure.Storage.Queues;using Newtonsoft.Json.Linq;namespace FreProcessorJob{ public class Functions { // Diese Funktion wird ausgelöst/ausgeführt, wenn eine neue Nachricht // in eine Azure Queue mit dem Namen processing-queue geschrieben wird // Die Nachricht muss eine JSON-Nachricht mit dem Schlüssel 'blob-item-name' sein // Verarbeitungsergebnisse werden im Container processing gespeichert // der Verarbeitungsstatus wird im JSON-Format an status-queue gesendet public static void ProcessQueueMessage([QueueTrigger("processing-queue")] string message) { // Zuerst Verbindung mit status-queue herstellen QueueClient queueClient = new QueueClient(Config.ConnectionString, Config.StatusQueueName); try { // Dies wird in den WebJob-Protokollen im Azure-Portal protokolliert Console.WriteLine("Accepted task: " + message); JObject task = JObject.Parse(message); task["processor_id"] = Environment.GetEnvironmentVariable("WEBJOBS_NAME"); // Dies wird an status-queue gesendet task["status"] = "accepted"; queueClient.SendMessage(task.ToString()); // blob-item-name abrufen – den Namen der Datei im Processing-Container string blobFileName = task["blob-item-name"].ToString(); BlobClient blobClient = new BlobClient(Config.ConnectionString, Config.ProcessingContainerName, blobFileName); // Blob in den Arbeitsspeicher laden Console.WriteLine("\t Downloading blob to memory: " + blobFileName); MemoryStream memoryStream = new MemoryStream(); blobClient.DownloadTo(memoryStream); Console.WriteLine("\t Downloaded."); // Status auf processing setzen Console.WriteLine("\t Processing in FRE: " + blobFileName); task["status"] = "processing"; queueClient.SendMessage(task.ToString()); // Den heruntergeladenen Blob in ABBYY FineReader Engine mit In-Memory-Verarbeitungsmethoden verarbeiten // Die Ausgabe ist der Name des Verarbeitungsergebnisses, das als Blob in <ProcessingContainerName> gespeichert wird string resultBlobName = ""; using (FreProcessor.Processor freProcessor = new FreProcessor.Processor()) { resultBlobName = freProcessor.ProcessBlobFromMemory(memoryStream, blobFileName); Console.WriteLine("\t Result blob name in output container: " + resultBlobName); } // Eingabebild löschen Console.WriteLine("\t Deleting from input container: " + blobFileName); blobClient.Delete(); // Status auf succeeded setzen // in den Azure-Portal-Protokollen Console.WriteLine("Succeeded"); // in status-queue task["status"] = "succeeded"; task["result-blob-name"] = resultBlobName; queueClient.SendMessage(task.ToString()); } catch (Exception error) { // Im Fehlerfall Meldung // an die Azure-Portal-Protokolle Console.WriteLine("Failed: " + error.Message); // an status-queue JObject task = new JObject(); task["processor_id"] = Environment.GetEnvironmentVariable("WEBJOBS_NAME"); task["status"] = "failed"; task["error"] = error.Message; task["task"] = message; queueClient.SendMessage(task.ToString()); } } }}
Program.cs
using Microsoft.Azure.WebJobs;namespace FreProcessorJob{ // Weitere Informationen zum Microsoft Azure WebJobs SDK finden Sie unter https://go.microsoft.com/fwlink/?LinkID=320976 class Program { // Legen Sie die folgenden Verbindungszeichenfolgen in app.config fest, damit diese WebJobs ausgeführt werden können: // AzureWebJobsDashboard und AzureWebJobsStorage static void Main() { var config = new JobHostConfiguration(); if (config.IsDevelopment) { config.UseDevelopmentSettings(); } // ABBYY FineReader Engine ist nicht threadsicher, daher können wir nicht mehr als eine Nachricht gleichzeitig verarbeiten config.Queues.BatchSize = 1; var host = new JobHost(config); // Der folgende Code stellt sicher, dass der WebJob kontinuierlich ausgeführt wird, // da eine der Funktionen an die Azure Queue gebunden ist und auf neue Aufgaben wartet host.RunAndBlock(); } }}
EngineLoader.cs
using System;using System.IO;using System.Runtime.InteropServices;using FREngine;namespace FreProcessorJob.FreProcessor{ // Klasse zum Laden/Entladen von FREngine.dll und zum Initialisieren/Deinitialisieren der Engine // Das Laden erfolgt im Konstruktor, das Entladen in Dispose() // Löst Ausnahmen aus, wenn das Laden fehlschlägt public class EngineLoader : IDisposable { // ABBYY FineReader Engine mit den in SamplesConfig.cs gespeicherten Einstellungen laden public EngineLoader() { string enginePath = Path.Combine(Config.GetEngineFolder(), "FREngine.dll"); string customerProjectId = Config.GetCustomerProjectId(); string licensePath = Path.Combine(Config.GetEngineFolder(), Config.GetLicenseTokenName()); string licensePassword = Config.GetLicensePassword(); try { // FREngine.dll-Bibliothek laden dllHandle = LoadLibraryEx(enginePath, IntPtr.Zero, LOAD_WITH_ALTERED_SEARCH_PATH); if (dllHandle == IntPtr.Zero) { int error = Marshal.GetLastWin32Error(); Console.WriteLine("Der letzte Win32-Fehler war: " + error); throw new Exception("Kann nicht geladen werden: " + enginePath); } IntPtr initializeEnginePtr = GetProcAddress(dllHandle, "InitializeEngine"); if (initializeEnginePtr == IntPtr.Zero) { throw new Exception("Funktion InitializeEngine nicht gefunden"); } IntPtr deinitializeEnginePtr = GetProcAddress(dllHandle, "DeinitializeEngine"); if (deinitializeEnginePtr == IntPtr.Zero) { throw new Exception("Funktion DeinitializeEngine nicht gefunden"); } IntPtr dllCanUnloadNowPtr = GetProcAddress(dllHandle, "DllCanUnloadNow"); if (dllCanUnloadNowPtr == IntPtr.Zero) { throw new Exception("Funktion DllCanUnloadNow nicht gefunden"); } // Zeiger in Delegates umwandeln initializeEngine = (InitializeEngine)Marshal.GetDelegateForFunctionPointer( initializeEnginePtr, typeof(InitializeEngine)); deinitializeEngine = (DeinitializeEngine)Marshal.GetDelegateForFunctionPointer( deinitializeEnginePtr, typeof(DeinitializeEngine)); dllCanUnloadNow = (DllCanUnloadNow)Marshal.GetDelegateForFunctionPointer( dllCanUnloadNowPtr, typeof(DllCanUnloadNow)); // Funktion InitializeEngine aufrufen string dataFolder = Path.Combine(Config.LibraryFolder, "AppData"); string tempFolder = Path.Combine(Config.LibraryFolder, "Temp"); int hresult = initializeEngine(customerProjectId, licensePath, licensePassword, dataFolder, tempFolder, false, ref engine); Marshal.ThrowExceptionForHR(hresult); } catch (Exception) { // FREngine.dll-Bibliothek freigeben engine = null; // Alle Objekte vor dem FreeLibrary-Aufruf löschen GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); FreeLibrary(dllHandle); dllHandle = IntPtr.Zero; initializeEngine = null; deinitializeEngine = null; dllCanUnloadNow = null; throw; } } // ABBYY FineReader Engine entladen public void Dispose() { if (engine == null) { // Engine wurde nicht geladen return; } engine = null; int hresult = deinitializeEngine(); // Alle Objekte vor dem FreeLibrary-Aufruf löschen GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); hresult = dllCanUnloadNow(); if (hresult == 0) { FreeLibrary(dllHandle); } dllHandle = IntPtr.Zero; initializeEngine = null; deinitializeEngine = null; dllCanUnloadNow = null; // Ausnahme nach der Bereinigung auslösen Marshal.ThrowExceptionForHR(hresult); } // Gibt einen Zeiger auf das Hauptobjekt der ABBYY FineReader Engine zurück public IEngine Engine { get { return engine; } } // Kernel32.dll-Funktionen [DllImport("kernel32.dll")] private static extern IntPtr LoadLibraryEx(string dllToLoad, IntPtr reserved, uint flags); private const uint LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008; [DllImport("kernel32.dll")] private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); [DllImport("kernel32.dll")] private static extern bool FreeLibrary(IntPtr hModule); // FREngine.dll-Funktionen [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] private delegate int InitializeEngine(string customerProjectId, string licensePath, string licensePassword, string dataFolder, string tempFolder, bool isSharedCPUCoresMode, ref FREngine.IEngine engine); [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate int DeinitializeEngine(); [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate int DllCanUnloadNow(); // Private Variablen private FREngine.IEngine engine = null; // Handle für FREngine.dll private IntPtr dllHandle = IntPtr.Zero; private InitializeEngine initializeEngine = null; private DeinitializeEngine deinitializeEngine = null; private DllCanUnloadNow dllCanUnloadNow = null; }}
IFileWriter.cs
using System;using System.IO;using Azure.Storage.Blobs;namespace FreProcessorJob.FreProcessor{ 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 <ProcessingContainerName> herstellen. Dort wird das Verarbeitungsergebnis gespeichert BlobClient resultBlobClient = new BlobClient(Config.ConnectionString, Config.ProcessingContainerName, resultBlobName + fileExtension); // Vorhandene Datei überschreiben resultBlobClient.DeleteIfExists(); // Position auf 0 setzen, um die Datei vom Anfang an zu schreiben stream.Position = 0; resultBlobClient.Upload(stream); stream.Close(); } public void Dispose() { // Speicherstream bei der Freigabe schließen, damit nach dem Schreiben der Daten darauf zugegriffen werden kann stream.Close(); } private string resultBlobName; private string fileExtension; private MemoryStream stream; }}
Processor.cs
using System;using System.Runtime.InteropServices;using System.IO;using FREngine;namespace FreProcessorJob.FreProcessor{ class Processor : IDisposable { private EngineLoader engineLoader = null; private void displayMessage(string text) { Console.WriteLine("\t" + text); } private void setupFREngine() { displayMessage("Vordefiniertes Profil wird geladen..."); // dies ist optional engineLoader.Engine.LoadPredefinedProfile("DocumentConversion_Accuracy"); // dies ist bei leistungsschwachen App Service-Plänen erforderlich, da es sonst bei der parallelen Verarbeitung zu Fehlern kommt engineLoader.Engine.MultiProcessingParams.MultiProcessingMode = MultiProcessingModeEnum.MPM_Sequential; } private void LoadEngine() { try { if (engineLoader == null) { engineLoader = new EngineLoader(); } setupFREngine(); } catch (Exception error) { displayMessage("Fehler: " + error.Message); } } private void UnloadEngine() { try { if (engineLoader != null) { engineLoader.Dispose(); engineLoader = null; } } catch (Exception error) { displayMessage("Fehler: " + error.Message); } } public Processor() { LoadEngine(); } public string ProcessBlobFromMemory(MemoryStream inputMemoryStream, string inputBlobName) { FRDocument document = engineLoader.Engine.CreateFRDocument(); string resultBlobName = ""; try { document.PageFlushingPolicy = FREngine.PageFlushingPolicyEnum.PFP_KeepInMemory; // Bilddatei zum Dokument hinzufügen displayMessage("Bilddatei wird geladen..."); IntPtr handle = Marshal.AllocHGlobal(inputMemoryStream.GetBuffer().Length); Marshal.Copy(inputMemoryStream.GetBuffer(), 0, handle, inputMemoryStream.GetBuffer().Length); document.AddImageFileFromMemory(handle.ToInt64(), null, null); // Dokument erkennen displayMessage("Erkennung läuft..."); document.Process(null); // Ergebnisse speichern displayMessage("Ergebnisse werden gespeichert..."); FileWriter fileWriter = new FileWriter(inputBlobName, ".pdf"); resultBlobName = inputBlobName + ".pdf"; document.ExportToMemory(fileWriter, FREngine.FileExportFormatEnum.FEF_PDF, null); } catch (Exception error) { displayMessage("Fehler: " + error.Message); throw error; } finally { // Dokument schließen document.Close(); } return resultBlobName; } public void Dispose() { UnloadEngine(); } }}