2022年8月26日金曜日

CKEditor5による画像アップロードを実装する(4) - 画像をオブジェクト・ストレージに保存する

以前にLouis Moreauxさんの記事を読んでCKEditor5で画像の貼り付けをできるようにしました。以下の記事です。

CKEditor5による画像アップロードを実装する(1) - 準備
CKEditor5による画像アップロードを実装する(2) - REST APIの作成
CKEditor5による画像アップロードを実装する(3) - 完成

さらにLouis MoreauxさんはCKEditor5 Image Upload - Part 3として、画像をデータベースではなくオブジェクト・ストレージに保存する方法を記事として公開しています。

CKEditor5に画像を含めると、文書にを表示する際に含まれている画像を同時にダウンロードするようです。そのため、この機能を組み込んだAPEXアプリケーションをリソース制限の厳しいAlways FreeのAutonomous Database上で動かすと、データベースのセッション数の上限に達してしまいます。

少し残念だったので、画像をオブジェクト・ストレージに保存するように変更して、少ないリソースでもアプリが使えるかどうか確認してみます。

以下は、事前に実施する準備作業です。

ユーザー、グループ、ポリシーなどの作成を行います。事前承認リスエストを生成するため、バケットの権限はreadではなくmanageが必要です。

Allow group APEXObjectManagers to read buckets in compartment APEX
Allow group APEXObjectManagers to manage objects in compartment APEX

APEXからOCIオブジェクト・ストレージを操作する(1) - APIユーザーの作成

バケットはapex_file_storageの代わりにimagesを作成します。

REST APIを呼び出す際に指定するWeb資格証明OCI_API_ACCESSとして作成します。

APEXからOCIオブジェクト・ストレージを操作する(4) - Web資格証明の作成

OCI側の準備は以上になります。これから画像をオブジェクト・ストレージに保存するように、アプリケーションに変更を加えます。


RESTサービスのPOSTハンドラの変更



画像のアップロードを受け付けて、BLOBに保存している処理をオブジェクト・ストレージに保存するように変更します。CKEditor5に戻すURLとしてRESTサービスのGETハンドラではなく、オブジェクト・ストレージにアップロードした画像のURLを返します。

ネームスペース名は適切な値に置き換えます。




置換文字列の準備



コード中で使用する各種パラメータを置換文字列として定義します。

G_OBJECT_STORAGE_URLとして、以下を設定します。リージョン名ネームスペース名バケット名の値を置き換えます。

https://objectstorage.リージョン名.oraclecloud.com/n/ネームスペース名/b/バケット名/o/

ネームスペース名のハードコードを避けるために、置換文字列G_NAMESPACEとしてネームスペース名を設定します。



事前承認リクエストの発行



画像をアップロードするときはクリデンシャルを指定していますが、画像を表示する(ダウンロードする)ときはクリデンシャルを指定する方法がありません。バケット可視性プライベートにした上で画像をダウンロードするために、事前承認リクエストを発行します。

事前承認リクエストのパスを保存するアプリケーション・アイテムG_PREAUTH_URLを作成します。

名前G_PREAUTH_URL有効範囲アプリケーションセキュリティセッション・ステート保護は一番厳しい制限付き - ブラウザからの設定不可を選びます。


このアプリケーション・アイテムに値を設定するアプリケーション・アイテムの計算を作成します。

事前承認リクエストのURLを取得するために、オブジェクト・ストレージのREST APIを呼び出します。

計算アイテムとしてG_PREAUTH_URLを選択します。頻度計算ポイント認証後を選択します。計算タイプファンクション本体計算として上記のコードを記述します。



対話モード・レポートのソース変更



対話モード・レポートのソースSQL問合せを以下のSELECT文に変更します。
select
    ID
    , TITLE
    -- , CONTENT
    , replace(CONTENT, :G_OBJECT_STORAGE_URL, :G_PREAUTH_URL) content
    , AUTHOR
    , PUBLISHED
    , CREATED
    , CREATED_BY
    , UPDATED
    , UPDATED_BY
from RTE_DOCUMENTS
以前はapex_session=の引数を画像URLに付加し、APEXセッションでORDSへのGETリクエストの認証を通していました。この部分を、事前承認済リクエストのURLに置き換えています。



editingDowncastコンバーターの変更



CKEditor5内で画像を表示するときは、editingDowncastコンバーターを構成し、画像URLを見つけてapex_session引数を追加していました。今回はその代わりに、見つけた画像URLを事前承認済リクエストのURLに置き換えています。


ページ・アイテムP2_CONTENT詳細JavaScript初期化コードを入れ替えます。



画像の紐付けの変更


ドキュメントに含まれるイメージの参照先が、G_IMAGE_URLからG_OBJECT_STORAGE_URLに変更されています。文書と画像を紐づけているプロセス画像の紐付けPL/SQLコードを以下に変更します。




外部キー制約の変更



表RTE_IMAGESには外部キー制約RTE_IMAGES_DOCUMENT_ID_FKが設定されていて、親である表RTE_DOCUMENTSから文書が削除されると、CASCADE設定により含まれる画像も削除されるようになっていました。オブジェクト・ストレージに保存した画像は自動的には保存されないため、CASCADEからON DELETE SET NULLに変更します。
alter table "RTE_IMAGES" drop constraint "RTE_IMAGES_DOCUMENT_ID_FK";
alter table "RTE_IMAGES" add constraint "RTE_IMAGES_DOCUMENT_ID_FK"
foreign key ("DOCUMENT_ID") references "RTE_DOCUMENTS" ("ID") on delete set null; 
表RTE_IMAGESの列DOCUMENT_IDがNULLの画像は、親となる文書が存在していません。定期的なハウスキーピングのジョブにて、行の削除と同時にオブジェクト・ストレージ上の画像の削除を行なうようにするよ良いでしょう。

以上でCKEditor5による画像のアップロード先/ダウンロード先をオブジェクト・ストレージに変更できました。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/ORDS_REST_WKSP_APEXDEV_rteditor_2023_07_04_2.sql
https://github.com/ujnak/apexapps/blob/master/exports/ckeditor-image-sample-obs.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。