記事
· 2021年10月28日 8m read

REST経由でファイル転送しプロパティに格納する - パート1

InterSystems開発者コミュニティにおいて、CachéアプリケーションへのTWAINインターフェースの作成の可能性に関する質問が上がりました。 Webクライアントの撮像装置からサーバーにデータを取得し、そのデータをデータベースに保管する方法について、素晴らしい提案がいくつかなされました。 

しかし、こういった提案を実装するには、Webクライアントからデータベースサーバーにデータを転送し、受信データをクラスプロパティ(または質問のケースで言えばテーブルのセル)に格納できなければなりません。 この方法は、TWAINデバイスから受信した撮像データを転送するためだけでなく、ファイルアーカイブや画像共有などの整理といったほかのタスクにも役立つ可能性があります。 

そこで、この記事では主に、HTTP POSTコマンドの本体から、raw状態またはJSON構造にラップしてデータを取得するRESTfulサービスを記述する方法を説明することにします。

RESTの基本

具体的な話に入る前に、まずREST全般と、IRISでRESTfulサービスがどのように作成されるかについて簡単に説明しましょう。

Representational state transfer(REST)は、分散ハイパーメディアシステムのためのアーキテクチャスタイルです。 RESTにおける情報の重要な抽象化は、適切な識別子を持ち、JSON、XML、またはサーバーとクライアントの両方が認識するほかの形式で表されるリソースです。 

通常、クライアントとサーバー間でのデータの転送にはHTTPが使用されます。 CRUD(作成、読み取り、更新、削除)操作ごとに独自のHTTPメソッド(POST、GET、PUT、DELETE)があり、リソースまたは一連のリソースには独自のURIがあります。

この記事では、POSTメソッドのみを使用して新しい値をデータベースに挿入するため、その制約を知る必要があります。 

IETF RFC7231 4.3.3 POST 」の仕様によると、POSTには本文に格納されるデータサイズに関する制限はありません。 しかし、Webサーバーやブラウザでは独自の制限が適用され、通常は1 MBから2 GBとなっています。 たとえば、Apacheの制限は最大2 GBとなっています。 いずれにせよ、2 GBのファイルを送信する必要がある場合は、アプローチを考え直すことをお勧めします。

IRISにおけるRESTfulサービスの要件

IRISでRESTfulサービスを実装するには、次を行う必要があります。

  • 抽象クラス%CSP.RESTを拡張するクラスブローカーを作成します。 これは逆に、%CSP.Pageを拡張するため、さまざまな便利なメソッド、パラメーター、オブジェクト、特に%requestへのアクセスが可能になります。
  • ルートを定義するUrlMapを指定します。
  • オプションとしてUseSessionパラメーターを設定し、各REST呼び出しが独自のWebセッションで実行されるのか、ほかのREST呼び出しで1つのセッションを共有するのかを設定します。
  • ルートで定義された演算を実行するクラスメソッドを提供します。
  • CSP Webアプリケーションを定義し、そのセキュリティをWebアプリケーションページ(システム管理 > セキュリティ > アプリケーション > Webアプリケーション)に指定します。Dispatchクラスには、ユーザークラスの名前とNameが格納されています。これはREST呼び出しのURLの最初の部分です。
  • 一般に、大きなデータ(ファイル)のチャンクとメタデータをクライアントからサーバーに送信するには、次のようにいくつかの方法があります。

  • ファイルとメタデータをBase64-encodeで暗号化し、サーバーとクライアントの両方に暗号化/復号化を行うための処理オーバーヘッドを追加します。
  • 最初にファイルを送信し、クライアントにIDを返します。すると、そのIDを持つメタデータが送信されます。 サーバーはファイルとメタデータをもう一度関連付けます。
  • 最初にメタデータを送信し、クライアントにIDを返します。すると、そのIDのファイルが送信されます。サーバーはファイルとメタデータをもう一度関連付けます。
  • この第1回目の記事では、基本的に2つ目のアプローチを使用しますが、クライアントへのIDの送信とメタデータ(別のプロパティとして格納するためのファイル名)の追加については、このタスクでは特に意味を持たないため、行いません。 第2回目の記事では、最初のアプローチを使用しますが、私のファイルとメタデータをサーバーに送信する前にJSON構造にパッケージ化します。

    RESTfulサービスの実装

    では、具体的な内容に入りましょう。 まず、設定しようとしているクラスとプロパティを定義しましょう

    Class RestTransfer.FileDesc Extends %Persistent
    {
      Property File As %Stream.GlobalBinary;
      Property Name As %String;
    }
    

    もちろん、通常はファイルに使用するメタデータがほかにもありますが、私たちの目的にはこれで十分です。

    次に、クラスブローカーを作成する必要があります。これは、ルートとメソッドを使用して後で拡張します。

    Class RestTransfer.Broker Extends %CSP.REST
    {
      XData UrlMap
      {
        <Routes>
        </Routes>
      }
    }
    

    そして最後に、前段階のセットアップとして、このアプリケーションをWebアプリケーションのリストに指定する必要があります。

    前段階のセットアップが完了したので、RESTクライアント(Advanced REST Clientを使用します)から受信するファイルをクラスRestTransfer.FileDescのインスタンスのFileプロパティとしてデータベースに格納するメソッドを記述できるようになりました。

    では、POSTメソッドの本体にRawデータとして格納されている情報から始めましょう。 まず、新しいルートをUrlMapに追加しましょう。

    <Route Url="/file" Method="POST" Call="InsertFileContents"/>

    このルートは、サービスがURL/RestTransfer/fileでPOSTコマンドを受信すると、InsertFileContentsクラスメソッドを呼び出すことを指定します。 より簡単に行うために、このメソッドの中にクラスRestTransfer.FileDescの新しいインスタンスを作成して、そのFileプロパティを受信データに設定します。 これにより、ステータスと、成功またはエラーのいずれかを示すJSON形式のメッセージが返されます。 以下はそのクラスメソッドです。

    ClassMethod InsertFileContents() As %Status
    {
      Set result={}
      Set st=0    
      set f = ##class(RestTransfer.FileDesc).%New()
      if (f = $$$NULLOREF) {
        do result.%Set("Message","Couldn't create an instance of the class")
      } else {
        set st = f.File.CopyFrom(%request.Content)
        If $$$ISOK(st) {
          set st = f.%Save()
          If $$$ISOK(st) {
            do result.%Set("Status","OK")
          } else {
            do result.%Set("Message",$system.Status.GetOneErrorText(st))
          }
        } else {
          do result.%Set("Message",$system.Status.GetOneErrorText(st))
        }
      }
      write result.%ToJSON()
      Quit st
    }
    

    まず、クラスRestTransfer.FileDescの新しいインスタンスを作成し、正常に作成されたこととOREFがあることをチェックします。 オブジェクトを作成できなかった場合は、JSON構造を作成します。 

    {"Message", "Couldn't create an instance of class"}

    オブジェクトが作成された場合は、リクエストのコンテンツがプロパティFileにコピーされます。 コピーに成功しなかった場合は、エラーの説明を含むJSON構造を作成します。 

    {"Message",$system.Status.GetOneErrorText(st)}

    コンテンツがコピーされた場合は、オブジェクトを保存し、保存に成功した場合は、JSON {"Status","OK"}を作成します。 そうでない場合、JSONはエラーの説明を返します。 

    最後に、このJSONをレスポンスに書き込んで、ステータスstを返します。

    画像を転送する例を次に示します。

    データベースへの保存方法:

    このストリームをファイルに保存して、変更されずに転送されたことを確認できます。

    set f = ##class(RestTransfer.FileDesc).%OpenId(4)
    set s = ##class(%Stream.FileBinary).%New()
    set s.Filename = "D:\Downloads\test1.jpg"
    do s.CopyFromAndSave(f.File)
    

    テキストファイルでも同じことができます。

    これはグローバルでも表示可能です。

    また、実行可能ファイルやその他の種類のデータを保存できます。

    グローバルでサイズを確認できます。正しいサイズを確認できます。

    この部分は正常に動作するようになったので、2番目のアプローチを確認することにしましょう。ファイルのコンテンツとJSON形式で格納されているメタデータ(ファイル名)を取得し、POSTメソッドの本体で転送するアプローチです。 

    RESTサービスの作成についての詳細は、InterSystemsのドキュメントをご覧ください。 

    両方のアプローチのサンプルコードはGitHubInterSystems Open Exchangeにあります。 いずれかに関連する質問や提案があれば、お気軽にコメント欄に書き込んでください。

    ディスカッション (0)2
    続けるにはログインするか新規登録を行ってください