メインコンテンツへスキップ
このセクションでは、FRE 12 for Windows アプリケーションを Azure Cloud Service にデプロイする方法を説明します。例として、Azure Storage アカウント内のコンテナーでファイルを処理する Worker を取り上げます。 このシナリオを実装するには、*.abbyy.com のライセンスサーバーに接続する オンライン ライセンス を使用します。
ABBYY Licensing Service で同時に使用できるオンライン ライセンスは 1 つだけです。
アプリケーションを Cloud Service にデプロイするには、いくつかの手順があります。
  1. 前提条件 に従って、オンライン ライセンス、ローカル マシン、およびクラウド インスタンスを準備する
  2. アプリケーションをデプロイする前に 準備手順 を実行する
  3. アプリケーションを Cloud Service にデプロイする
文書処理で ABBYY FineReader Engine のメソッドを使用する方法を示した コード サンプル を参照してください。

前提条件

オンライン ライセンス

オンライン ライセンスを使用するには、営業担当者から次の情報を受け取る必要があります。
  • Customer Project ID
  • オンライン ライセンス トークン ファイル
  • ライセンス トークン ファイルのパスワード
ABBYY Licensing Service がインストールされている環境でオンライン ライセンスを使用するには、次の条件を満たしている必要があります。
  • 有効なインターネット接続
  • ポート 443 (HTTPS) で *.abbyy.com への接続が許可されていること
  • 認証局の GoDaddy ルート証明書 (この証明書は、信頼されたルート証明機関の証明書ストアのローカル マシン ストアにインストールされている必要があります。証明書の詳細については、GoDaddy のウェブサイトを参照してください。)

ローカル マシン

Cloud Service を作成する前に、以下の仕様に従ってローカル マシンを準備してください。
  • Visual Studio 2019 と、Azure 向けアプリケーション開発用モジュール (Visual Studio の Azure 機能を確認するか、Visual Studio Installer を使用してこれらのモジュールをダウンロードしてください)
  • デバッグを容易にするための Cloud Service エミュレーターおよびストレージ (こちらの情報を参照し、Visual Studio Installer 経由でダウンロードしてください)
  • 単一の Worker role プロジェクトを含む Azure Cloud Service (拡張プレビュー) 用のテンプレート ソリューション (こちらの情報を参照)
  • .NET Framework 4.7.2
  • Azure SDK (こちらからダウンロード)
  • Azure Storage および Blob コンテナーを操作するための NuGet パッケージ - Azure.Storage.Blobs (こちらからダウンロード)
  • .NET Framework 4.7 用 ABBYY FineReader Engine wrapper (開発者向けインストール後、C:\ProgramData\ABBYY\SDK\12\FineReader Engine\Inc.NET interops フォルダー内)
  • Blob コンテナーを操作するためにオーバーライドした IFileWriter インターフェイス (以下のsampleを参照)
  • Azure Storage Explorer (任意 - こちらからダウンロード)

Cloud Service インスタンス

Cloud Service インスタンスは、Azure で WorkerRole プロジェクトを格納するために使用します。このインスタンスは、次の仕様で構成します。
  • Service の .NET Framework バージョン
  • ABBYY FineReader Engine のデプロイに使用する PowerShell
  • Azure SDK のアップロードに使用する NuGet 2.8.5.201 以降
  • PowerShell を使用して Azure Storage アカウントを操作するための Azure SDK

事前準備

事前準備はローカルマシンで行います。これらの手順を完了すると、アプリケーションのデプロイを開始するために必要な設定とファイルをすべて用意できます。
  1. ABBYY FineReader Engine ライブラリと Licensing Service を含む 2 つのアーカイブ (たとえば、LibraryPackage.zip と LSPackage.zip) を作成します。FREngineDistribution.csv ファイルを使用すると、ファイル一覧を自動的に作成できます。ABBYY FineReader Engine と ライセンスサーバー には同じパッケージのものを使用してください。そうしないと、互換性は保証されません。
  2. Azure Storage アカウント (この記事では frestorage) を作成します。必要な手順はすべて Azure の Web サイトに記載されています。
  3. frestorage 内に 3 つの Blob コンテナーを作成します。
  • fre-lib - ABBYY FineReader Engine のファイル用
  • fre-input - 入力ファイル用
  • fre-output - 処理結果用
  1. LibraryPackage.zip と LSPackage.zip を、最も使いやすい方法 (.NET、Powershell、Python スクリプト、または Azure Storage Explorer / Azure Portal アプリケーションの使用) で fre-lib コンテナーにアップロードします。
例として、構成済み環境で作業するために WorkerRole プロジェクトを使用します。必要な構成ファイル (.csdef と Cloud.cscfg) は、プロジェクトの作成後に自動的に生成されます。

Cloud Service への ABBYY FineReader Engine のデプロイ

新しい WorkerRole プロジェクトに ABBYY FineReader Engine をデプロイするには、次の手順を実行します。
  1. 必要に応じて Cloud.cscfg のパラメーターを指定します。
  2. .csdef ファイルのパラメーターを指定します。
  • (任意) WorkerRole の設定
  • (任意) 仮想マシンのサイズ
  • (必須) ロールのローカル ストレージ (LocalStorage セクション。この資料では、LocalStorage1 という名前のストレージを使用) 。ABBYY FineReader Engine パッケージを正しくデプロイできるよう、少なくとも 3 GB に設定します。
  • (必須) ロールの起動順序 (コード サンプル セクションのコード サンプルと設定を使用)
  1. fre-input コンテナー内のファイルを処理し、処理結果を fre-output コンテナーに出力するように WorkerRole プロジェクトを実装します。
  • Cloud Service を動作可能な状態に準備するための OnStart メソッド。このメソッドは、Engine を初期化し、TLS プロトコルを設定するために使用します。
  • fre-input コンテナーから取得したファイルを繰り返し処理するための RunAsync メソッド。このメソッドは、fre-input コンテナー内のファイルを検出し、メモリ内で処理した後、fre-output コンテナーに配置します (Processor.csIFileWriter.csConfig.cs を参照) 。
  • Cloud Service の動作を終了するための OnStop メソッド。このメソッドは、Engine の初期化を解除するために使用します。

コード サンプル

このセクションには、ロールの起動順序と ABBYY FineReader Engine の設定に使用するコード サンプルが含まれています。
  • CleanUpOnStart - 以前のバージョンの ABBYY FineReader Engine を削除し、更新手順を簡略化します (下記の CleanUpOnStart.cmd ファイルおよび CleanUpOnStart.ps1 ファイルの内容を参照) 。
  • PreparePoShModules - PowerShell で Azure Storage を操作するために必要な SDK をダウンロードします (下記の PreparePoShModules.cmd ファイルおよび PreparePoShModules.ps1 ファイルの内容を参照) 。
  • PrepareLibrary - fre-lib コンテナーから ABBYY FineReader Engine ライブラリを取得し、ローカル ストレージに展開します (下記の PrepareLibrary.cmd ファイルおよび PrepareLibrary.ps1 ファイルの内容を参照) 。
  • PrepareLS - fre-lib コンテナーから Licensing Service を取得し、展開して起動します (下記の PrepareLS.cmd ファイルおよび PrepareLS.ps1 ファイルの内容を参照) 。
上記のスクリプトは、アプリケーションを起動する前に、示された順序どおりに正確に実行する必要があります。スクリプトの実行順序をカスタマイズするには、次の属性を指定します。
  • taskType=“simple” - タスクは同期的に、1 回に 1 つずつ実行されます。
  • executionContext=“elevated” - スタートアップ スクリプトを管理者権限で実行します (アプリケーションのインストールと LicensingService.exe の実行に必要です)
その結果、Cloud Service で管理可能なフォルダーが作成されます。このフォルダーは、上記のスクリプトで ABBYY FineReader Engine を配置し、さらに サンプル で使用する FREngine.dll をロードするために使用されます。
rem   以前の ABBYY FineReader Engine および 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 が開始されました...";
# ローカル ストレージへのパス(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 のプロセスが実行中の場合は停止
$processes = Get-Process;
foreach( $process in $processes ) {
    if( $process.Name -eq "LicesingService" ) {
        Stop-Process $process;
    }
}
 
# 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;
# Input フォルダーを削除して再作成
$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;
# Results フォルダーを削除して再作成
$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 ("リソース フォルダーをクリーンアップしました。");
rem   ストレージ アカウントに接続し、ABBYY FineReader Engine および Licensing Service のパッケージをダウンロードできるようにモジュールを準備するスクリプト
 
echo PoSh モジュールを準備しています >> ".\PreparePoShModules.log" 2>&1
PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\PreparePoShModules.ps1 >> ".\PreparePoShModules.log" 2>&1
echo PoSh モジュールの準備が完了しました。 >> ".\PreparePoShModules.log" 2>&1
 
exit /B %errorlevel%
# モジュールをインストール
Write-Host "PreparePoShModules.ps1 が開始されました...";
# Azure モジュールのダウンロードに必要な NuGet プロバイダーをインストール
Write-Host "NuGet パッケージ プロバイダーをインストールしています...";
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force;
 
# BLOB を操作するための Azure Storage モジュールをインストール
Write-Host "Az.Storage モジュールをインストールしています...";
Install-Module -Name Az.Storage -Scope CurrentUser -Repository PSGallery -Force -AllowClobber;
Write-Host ("PoSh モジュールの準備が完了しました。");
rem   ABBYY FineReader Engine パッケージを準備するスクリプト
echo ライブラリを準備しています >> ".\PrepareLibrary.log" 2>&1
PowerShell -ExecutionPolicy Unrestricted .\StartupScripts\PrepareLibrary.ps1 >> ".\PrepareLibrary.log" 2>&1
echo ライブラリの準備が完了しました。 >> ".\PrepareLibrary.log" 2>&1
exit /B %errorlevel%
Write-Host "PrepareLibrary.ps1 started...";
# ABBYY FineReader Engine および Licensing Service のパッケージを含むコンテナー
$container_name = 'fre-lib';
# ストレージ アカウントの接続文字列 
$connection_string = "<connection_string_to_frestorage>";
Write-Host "Configuration";
Write-Host ("Container name: " + $container_name);
Write-Host ("Connection string: " + $storage_account);
# ストレージ オブジェクトに接続
Write-Host ("Connecting to storage account...");
$storage_account = New-AzStorageContext -ConnectionString $connection_string;
# blob(実質的にはファイル)を取得
Write-Host ("Getting blobs from container...");
$fre_blobs = Get-AzStorageBlob -Container $container_name -Context $storage_account;
# ローカル ストレージへのパス(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';
# 必要な唯一の blob をダウンロード
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.");
    }
}
# ダウンロードしたパッケージを展開(既存のライブラリは強制的に上書き)
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   Licensing Service パッケージを準備し、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 started...";
# ABBYY FineReader Engine と Licensing Service のパッケージを含むコンテナー
$container_name = 'fre-lib';
# ストレージ アカウントの接続文字列
$connection_string = "<connection_string_to_frestorage>";
Write-Host "Configuration";
Write-Host ("Container name: " + $container_name);
Write-Host ("Connection string: " + $storage_account);
# ストレージ オブジェクトに接続
Write-Host ("Connecting to storage account...");
$storage_account = New-AzStorageContext -ConnectionString $connection_string;
# Blob(実質的にはファイル)を取得
Write-Host ("Getting blobs from container...");
$fre_blobs = Get-AzStorageBlob -Container $container_name -Context $storage_account;
# ローカル ストレージへのパス(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 パッケージをダウンロード
Write-Host ("Downloading Licensing Service package from container...");
foreach( $blob in $fre_blobs ) {
    Write-Host $blob.Name;
    if( $blob.Name -eq "LSPackage.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.");
    }
}
# ダウンロードしたパッケージを展開
Write-Host ("Unzipping library package...");
Expand-Archive -Path (Join-Path -Path $destination_path -ChildPath "LSPackage.zip") `
    -DestinationPath (Join-Path -Path $destination_path -ChildPath "LSPackage") -Force;
# ライセンス用のフォルダーを作成
$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 を起動
Write-Host ("Starting Licensing Service...");
 
$licensing_service_app = Join-Path -Path $destination_path -ChildPath "LSPackage\LicensingService.exe";
Start-Process -FilePath $licesing_service_app -ArgumentList "/standalone";
Write-Host ("LS package was prepared and LS was started.");
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() 
        {
            // <OutputContainerName> 内の新しい Blob への接続を作成
            // 処理結果はそこに保存される
            BlobClient resultBlobClient = new BlobClient(Config.ConnectionString, Config.OutputContainerName,
                resultBlobName + fileExtension);
            // 既存のファイルを上書き
            resultBlobClient.DeleteIfExists();
            // 先頭からファイルを書き込めるよう、位置を 0 に設定
            stream.Position = 0;
            resultBlobClient.Upload(stream);
 
            stream.Close();
        }
        public void Dispose()
        {
            // データ書き込み後にアクセスできるよう、破棄時にメモリ ストリームを閉じる
            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 is running");
 
            try
            {
                this.RunAsync(this.cancellationTokenSource.Token).Wait();
            }
            finally
            {
                this.runCompleteEvent.Set();
            }
        }
        public override bool OnStart()
        {
            // 同時接続の最大数を設定します
            ServicePointManager.DefaultConnectionLimit = 12;
 
            // ストレージ アカウントに接続できるように TLS 1.2 を設定します
            System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
 
            // 構成変更の処理について詳しくは、
            // https://go.microsoft.com/fwlink/?LinkId=166357 の MSDN トピックを参照してください
 
            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: 次のコードを独自のロジックに置き換えてください
            while (!cancellationToken.IsCancellationRequested)
            {
                Trace.TraceInformation("Working");
 
                try
                {
                    // コンテナを作成し、コンテナ クライアント オブジェクトを返します
                    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);
            }
        }
    }
}
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("Loading predefined profile...");
            loadProfile();
        }
        public void LoadEngine()
        {
            try {
                if (engineLoader == null)
                {
                    // Hello サンプルと同じ EngineLoader
                    engineLoader = new EngineLoader();
                }
                setupFREngine();
            }
            catch (Exception error)
            {
                displayMessage("error: " + error.Message);
            }
        }
        public void UnloadEngine()
        {
            try
            {
                if (engineLoader != null)
                {
                    engineLoader.Dispose();
                    engineLoader = null;
                }
            }
            catch (Exception error)
            {
                displayMessage("error: " + error.Message);
            }
        }
 
        public string ProcessImage( MemoryStream inputMemoryStream, string inputBlobName )
        {
            FRDocument document = engineLoader.Engine.CreateFRDocument();
            string resultBlobName = "";
            try
            {
               document.PageFlushingPolicy = FREngine.PageFlushingPolicyEnum.PFP_KeepInMemory;
 
                // 画像ファイルをドキュメントに追加
                displayMessage("Loading image...");
                IntPtr handle = Marshal.AllocHGlobal(inputMemoryStream.GetBuffer().Length);
                Marshal.Copy(inputMemoryStream.GetBuffer(), 0, handle, inputMemoryStream.GetBuffer().Length);
                document.AddImageFileFromMemory(handle.ToInt64(), null, null);
 
                // ドキュメントを認識
                displayMessage("Recognizing...");
                document.Process(null);
 
                // 結果を保存
                displayMessage("Saving results...");
                // 'balanced' シナリオを使用して結果を PDF に保存
                // 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("error: " + error.Message);
            }
            finally
            {
                // ドキュメントを閉じる
                document.Close();
            }
 
            return resultBlobName;
        }
 
        public void Dispose()
        {
            UnloadEngine();
        }
    }
}
class Config
{
    // ServiceDefinition.csdef で定義されているローカル ストレージ
    private static string LocalStorageName = "LocalStorage1";
    // ブロブ コンテナーへの接続文字列
    public static readonly string ConnectionString = "<connection_string_to_frestorage>";
    // ストレージ内の入力コンテナー名と出力コンテナー名
    public static readonly string InputContainerName = "fre-input";
    public static readonly string OutputContainerName = "fre-output";
 
    // ABBYY FineReader Engine の Customer Project ID を返す
    public static String GetCustomerProjectId()
    {
        return "<Your_Customer_Project_ID>";
    }
 
    // ライセンス トークン名を返す
    public static String GetLicenseTokenName()
    {
        return "<Token_number>.ABBYY.ActivationToken";
    }
 
    // ライセンス パスワードを返す
    public static String GetLicensePassword()
    {
        return "<Your_Online_License_token_password>";
    }
    // Engine のパスを返す
    public static String GetEngineFolder()
    {
        string engineSubfolder = "fre_packages\\LibraryPackage\\Bin64";
        string engineDllFolder = Path.Combine(RoleEnvironment.GetLocalResource(LocalStorageName).RootPath, engineSubfolder);
        return engineDllFolder;
    }
}