2022年9月5日月曜日

APEXアプリに埋め込む画像をオブジェクト・ストレージに配置する

 CKEditor5(リッチ・テキスト・エディタ)に表示する画像をオブジェクト・ストレージに配置し、事前認証リクエストを使って参照する実装について記事(CKEditor5による画像アップロードを実装する(4) - 画像をオブジェクト・ストレージに保存する)を書いています。

カード・リージョンやレポートに画像を表示する方がリッチ・テキスト・エディタに画像を埋め込むより用途としては多いはずなので、カードに表示する画像をオブジェクト・ストレージに配置するような実装を試してみました。


特にカード・リージョンで画像を表示する際に問題になりますが、データベースから画像をダウンロードさせると、アクティブなセッションをひとつ使います。イメージのサイズが大きいとダウンロードに時間がかかり、セッションの占有時間が伸びます。カード・リージョンでは複数のカードが一度に表示されます。また、同じカード・リージョンのページを開いているユーザーが複数いる場合、それだけでアクティブ・セッションの上限に達してしまい、他の操作ができなくなる可能性があります。画像のダウンロードをオブジェクト・ストレージから直接行うことにより、データベースのアクティブ・セッションの数を減らすことができます。

SQLワークショップユーティリティクイックSQLを使って、使用する表を作成します。

以下のモデルから、表IMG_ITEMSIMG_IMAGESを作成します。

# prefix: img
# pk: guid
# ondelete: set null
items
    name vc80 /nn

images
    item_id /fk items
    location vc400 /nn
    file_name vc80
    mime_type vc200
    last_updated date /nn
SQLの生成SQLスクリプトを保存レビューおよび実行を順次実施します。表の作成までを実施し、アプリケーションの作成は行いません。
 

アプリケーション作成ウィザードを起動します。

アプリケーションの名前画像ビューワーとします。ホーム・ページ編集を開いて削除し、代わりにファセット検索のページを追加します。


ページ名画像ビューワーとします。表示形式レポートを選択します。フォームの画面をウィザードで作成するためにレポートを選んでいます。フォームを含めるチェックを入れます。アプリケーションの作成後にレポートからカードに変更します。ソースとなるとしてIMG_ITEMSを選択します。

ページの追加をクリックします。


以上を設定し、アプリケーションの作成を実行します。

アプリケーションが作成されます。


オブジェクト・ストレージを扱うための設定を行います。記事CKEditor5による画像アップロードを実装する(4) - 画像をオブジェクト・ストレージに保存するで行なっている準備作業(APEXワークスペース・スキーマへの権限付与、クリデンシャルMY_OCI_CREDの作成、バケットimagesの作成等)は実施済みとします。

 アプリケーション定義置換として、置換文字列G_REGIONG_NAMESPACEG_BUCKETG_CREDENTIAL置換値を設定します。


記事CKEditor5による画像アップロードを実装する(4) - 画像をオブジェクト・ストレージに保存する事前承認リクエストの発行を参照し、アプリケーション・アイテムG_PREAUTH_URLを作成します。


アプリケーションの計算を作成します。計算として設定するコードは以下です。

declare
C_BASE_URL constant varchar2(200) := 'https://objectstorage.us-ashburn-1.oraclecloud.com/n/' || :G_NAMESPACE || '/b/images/p/';
l_request clob;
l_response clob;
l_response_json json_object_t;
l_access_uri varchar2(400);
begin
select json_object(
key 'accessType' value 'AnyObjectRead',
key 'name' value 'standard-' || sys_guid(),
key 'timeExpires' value systimestamp + interval '1' day
) into l_request from dual;
apex_web_service.clear_request_headers;
apex_web_service.set_request_headers('Accept', 'application/json', p_reset => false);
apex_web_service.set_request_headers('Content-Type', 'application/json', p_reset => false);
l_response := apex_web_service.make_rest_request(
p_url => C_BASE_URL
,p_http_method => 'POST'
,p_body => l_request
,p_credential_static_id => 'OCI_API_ACCESS'
);
l_response_json := json_object_t(l_response);
l_access_uri := l_response_json.get_string('accessUri');
return 'https://objectstorage.us-ashburn-1.oraclecloud.com' || l_access_uri;
end;
view raw preauth_url.sql hosted with ❤ by GitHub


レポートのページ(ページ番号1)を開き、リージョンImg Itemsを選択します。

識別タイプクラシック・レポートからカードに変更します。ソースタイプSQL問合せに変更し、SQL問合せを以下のSELECT文に置き換えます。

select i.id, i.name, :G_PREAUTH_URL || g.location location, g.file_name
from img_items i join
(
select id, item_id, location, file_name, mime_type from
(
select
row_number() over (partition by item_id order by last_updated desc) rn,
id, item_id, location, file_name, mime_type
from img_images
)
where rn = 1
) g on i.id = g.item_id


リージョンのタイプをカードに変更すると、ボタンCREATEが表示されなくなります。ボタンCREATEを選択し位置Region Bodyに変更します。


リージョンImg Itemsを選択し、プロパティ・エディタの属性を開きます。

タイトルとしてNAMEを選択します。メディアソースとしてURL列URL列としてLOCATIONを選択します。位置外観サイズ指定はデフォルトのまま変更しません。


カードの編集画面を開くため、カード・リージョンImg Itemsアクションを作成します。

作成したアクション識別タイプとしてカード全体を選択します。リンクタイプとしてこのアプリケーションのページにリダイレクトターゲットページ2とします。


ターゲットをクリックして、リンク・ビルダー・ターゲットを開いて設定します。

ターゲットページを選択します。アイテムの設定名前としてP2_IDとして&ID.を設定します。

以上でOKをクリックします。


ページ・デザイナにてページ番号2のフォームのページを開きます。イメージ・ファイルのアップロードを実装します。

Region Bodyにページ・アイテムP2_IMAGEを作成します。

識別名前P2_IMAGEタイプとしてファイル参照...を選択します。ラベルImageとします。設定表示形式としてBlock Dropzoneを選択します。

記憶域タイプとしてTable APEX_APPLICATION_TEMP_FILESを選択し、ファイルをパージするタイミングとしてEnd of Requestを選びます。アップロードされたファイルは表APEX_APPLICATION_TEMP_FILESの1行として保存されます。そのファイルは次に作成するプロセスにてオブジェクト・ストレージにアップロードするので、リクエストが終了したら不要になります。そのため、リクエスト終了時にパージする(End of Request)ように設定します。


オブジェクト・ストレージにアップロードするプロセスを作成します。

最初に以下のパッケージIMG_UTILを作成します。

/**
APEX_APPLICATION_TEMP_FILESにアップロードされた画像を、オブジェクト・ストレージに
アップロードする。
仕様部
*/
create or replace package img_util as
procedure upload_image(
p_id in number
,p_item_value in varchar2
,p_region in varchar2
,p_namespace in varchar2
,p_bucket in varchar2
,p_credential in varchar2
);
end img_util;
/
/**
パッケージ本体
*/
create or replace package body img_util as
procedure upload_image(
p_id in number
,p_item_value in varchar2
,p_region in varchar2
,p_namespace in varchar2
,p_bucket in varchar2
,p_credential in varchar2
)
as
l_image_id img_images.id%type;
l_file_name img_images.file_name%type;
l_location img_images.location%type;
l_mime_type img_images.mime_type%type;
l_blob blob;
l_put_response dbms_cloud_oci_obs_object_storage_put_object_response_t;
e_file_upload_exception exception;
begin
/*
* APEXの機能でアップロードされたファイルを取得する。
*/
select blob_content, filename, mime_type into l_blob, l_file_name, l_mime_type
from apex_application_temp_files
where name = p_item_value;
/*
* 仮のLOCATIONで、表IMG_IMAGESに行を挿入しIDを取得する。
* 取得したIDでオブジェクト・ストレージのオブジェクト名を決定します。
*/
insert into img_images(item_id, file_name, mime_type, location, last_updated)
values(p_id, l_file_name, l_mime_type, 'temp', sysdate)
returning id into l_image_id;
l_location := p_id || '/' || l_image_id || '/' || l_file_name;
/*
* 画像をオブジェクト・ストレージにアップロードする。
*/
l_put_response := dbms_cloud_oci_obs_object_storage.put_object
(
namespace_name => p_namespace
,bucket_name => p_bucket
,object_name => l_location
,content_type => l_mime_type
,put_object_body => l_blob
,region => p_region
,credential_name => p_credential
);
if l_put_response.status_code <> 200 then
raise e_file_upload_exception;
end if;
/*
* オブジェクト名と更新日を更新する。
*/
update img_images set location = l_location, last_updated = sysdate where id = l_image_id;
end upload_image;
end img_util;
/
view raw img_util.sql hosted with ❤ by GitHub

プロセスを作成し、プロセス・フォームImg Itemの下に配置します。識別名前オブジェクト・ストレージに保存とします。タイプAPIの呼出しを選びます。設定にてパッケージIMG_UTILのプロシージャUPLOAD_IMAGEを呼び出すように指定します。


サーバー側の条件タイプとしてリクエストは値に含まれるCREATE SAVEを指定します。


パラメータはすべてアイテムを割り当てます。それぞれ以下の設定になります。

p_id = P2_ID
p_item_value = P2_IMAGE
p_region = G_REGION
p_namespace = G_NAMESPACE
p_bucket = G_BUCKET
p_credential = G_CREDENTIAL

以上でアプリケーションは完成です。実行すると先頭のGIF動画のような動作をします。

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

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