Always FreeのAutonomous Databaseのストレージは20GBなので、ちょっとファイルをアップロードすると容量が足りなくなります。ファイルを保存するために、高速なストレージを消費するのも勿体無い話ですし、それだけのために有料インスタンスにアップグレードするのも勿体無い気がします。
とういうことで、ファイルの実体をオブジェクト・ストレージに保存するようにAPEXアプリケーションを改変してみます。
アップロードされたファイルは、オブジェクト・ストレージに以下のように保存されます。
準備
今までの記事に従って、ファイル管理アプリケーションが作成済みであることを前提としています。今回はオブジェクト・ストレージを使うため、以下の記事の記載に従って準備します。
バケットはapex_file_storageを使います。
表SFM_CONTENTSの変更
オブジェクト・ストレージにファイルを保存すると、全文検索索引の再作成ができなくなります。そのため、索引作成のソースとなる文字列を表SFM_CONTENTSに保存しておきます。
全文検索索引のソースを保存する列TEXT_INDEX_SOURCEを表SFM_CONTENTSに追加します。
alter table sfm_contents add (text_index_source blob);
また、変更回数を記録する列VERSIONも追加します。
alter table sfm_contents add (version number default 0 not null);
クイックSQLのモデルは以下のように変わります。
# prefix: sfm
# pk: guid
contents
title vc80 /nn
abstract vc800
content file
text_index_source blob
version num /default 0 /nn
ファイルから全文検索索引のソースとなる文字列を抽出するプロシージャSFM_CONTENTS_DOCは以下に変更します。
ファイルがデータベースにアップロードされたときに、ファイルから文字列を抽出し列TEXT_INDEX_SOURCEに保存していることが前提です。索引を作成する際にファイルのデータを必要としないため、ファイルがオブジェクト・ストレージにあっても全文検索索引を作ることができます。
全文検索索引のソースは文字データなので圧縮することで容量を節約していますが、表自体が圧縮されている場合は不要かもしれません。そうすると列TEXT_INDEX_SOURCEはCLOBで定義できますが、今回は手が込んでいる方がサンプルとして使い勝手が良いだろうと判断し、圧縮して保存するようにしています。
表SFM_CONTENTSに列が追加されたため、ページ番号3のフォームSfm Contentでページ・アイテムの同期化を実行すると、ページ・アイテムP3_TEXT_INDEX_SOURCEとP3_VERSIONが追加されます。このページ・アイテムにユーザーがデータを入力することは無いため、ビルド・オプションでコメント・アウトしておきます。
オブジェクト・ストレージのアクセス情報の設定
ファイルを保存するオブジェクト・ストレージに関する情報を、アプリケーション定義の置換文字列として設定します。
G_REGION - リージョン(今回の例ではus-ashburn-1)
G_NAMESPACE - オブジェクト・ストレージのネームスペース
G_BUCKET - バケット名(今回の例ではapex_file_storage)
G_CREDENTIAL - クリデンシャル(今回の例ではMY_OCI_CRED)
置換文字列の設定では前後の空白はトリムされないため、不要な空白が入っていないか注意が必要です。
プロシージャとパッケージの作成
列TEXT_INDEX_SOURCEにデータを保存するプロシージャretrieve_text_index_sourceを作成します。このデータが全文検索索引の作成時に使用されます。
SQLワークショップのSQLコマンドに貼り付けて実行します。
次に、オブジェクト・ストレージにファイルをアップロードするプロシージャupload_fileとダウンロードするプロシージャdownload_file、および削除するプロシージャdelete_fileを実装したパッケージsfm_file_utilを作成します。
コードは以下になります。
SQLワークショップのSQLスクリプトとして実行します。
確認画面が開くので、即時実行をクリックします。
パッケージとパッケージ本体が作成されたことを確認します。
アップロード処理の変更
ページ・デザイナにてページ番号3のフォームのページを開きます。
プロシージャretrieve_text_index_sourceを呼び出すプロセスを作成します。
プロセスの作成を実行し、新規に作成されたプロセスをプロセス・フォームSfm Contentの下に配置します。
識別の名前を文字列の抽出とし、タイプにAPIの呼び出しを選択します。設定のタイプとしてPL/SQL Procedure or Function、所有者をParsing Schemaとし、プロシージャまたはファンクションとしてRETRIEVE_TEXT_INDEX_SOURCEを選択します。
サーバー側の条件のタイプとしてリクエストは値に含まれるを選択し、値にCREATE SAVEを入力します。ボタン作成および変更の適用を押したとき(ボタン削除では実行しない)に、プロセス文字列の抽出を実行します。
パラメータp_idには、ページ・アイテムP3_IDの値を渡します。
同様にパッケージ・プロシージャupload_fileを呼び出すプロセスを作成します。
識別の名前はファイルのアップロード、設定のタイプはPL/SQL Package、パッケージはSFM_FILE_UTIL、プロシージャまたはファンクションとしてUPOAD_FILEを選択します。
このプロセスも、ボタン作成または変更の適用がクリックされたときに実行します。
パラメータp_idにはページ・アイテムP3_IDの値を渡します。それ以外は、置換文字列として設定したG_REGION、G_NAMESPACE、G_BUCKET、G_CREDENTIALの値を渡します。
アップロードするファイルが指定されていないときは、文字列の抽出とファイルのアップロードが実行されないよう、その前にダイアログを閉じます。
プロセスを作成し、プロセス・フォームSfm Contentの直下に配置します。
識別の名前をダイアログを閉じる - ファイル無し、タイプとしてダイアログを閉じるを選択します。サーバー側の条件のタイプとしてアイテムはNULLを選択し、アイテムとしてP3_CONTENTを選びます。
以上でオブジェクト・ストレージへのファイルのアップロードが実装できました。
アプリケーションからファイルをアップロードしても、列Cotentには何も表示されません。列CONTENTが空になっているためです。
ファイルの削除
ファイルが削除されたときに、オブジェクト・ストレージ上のファイルも削除します。
プロセスを作成します。識別の名前はファイルの削除とします。
プロセス・フォームSfm Contentの直下に配置します。パラメータの設定はファイルのアップロードと同じ設定になります。
設定のプロシージャまたはファンクションとしてDELETE_FILEを選択し、サーバー側の条件のボタン押下時としてDELETEを選択します。
ダウンロード処理の変更
列CONTENTの内容の代わりに、オブジェクト・ストレージ上のファイルをダウンロードするように処理を変更します。
ページ番号4のdownloadのページに作成したプロセスダウンロードを、パッケージSFM_FILE_UTILのDOWNOAD_FILEを呼び出すように変更します。
パラメータp_idへはアイテムIDを渡します。それ以外はアップロードのプロセスと同じく、オブジェクト・ストレージに関する置換文字列を指定します。
ファイルをオブジェクト・ストレージに配置しても、対話モード・レポートの列Download Urlは変わらず有効です。このURLからファイルをダウンロードできます。
ダウンロード・リンクの調整
対話モード・レポートからファイルをダウンロードする方法を変更します。
列CONTENTはつねに空なので、ソースとなるSELECT文より除きます。
列TITLEをクリックしてファイルのダウンロードが開始するよう、列の書式のHTML式として以下を記述します。
<a href="#DOWNLOAD_URL#&session=&APP_SESSION.">#TITLE#</a>
ページ番号3のフォームに含まれるページ・アイテムP3_CONTENTの設定のダウンロード・リンクの表示はOFFに変更します。
以上でアプリケーションは完成です。
今回作成した作成したアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/simple-file-manager-obs.zip
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完