こちらの記事の続きになります。すでにアプリケーションが作成済み、ページ作成ウィザードにより、ページ番号6としてデータのロードを行うページが存在していることを前提とします。
以下より、変更作業について記述します。
表FLD_FILESの列CONTENTにファイルをアップロードするよう、リージョンファイルのアップロードを静的コンテンツからフォームへ変更し、BLOBへの保存を行うプロセスを追加します。
タイプをフォームに変更し、ソースの表名にFDL_FILESを選択します。サーバー側の条件のアイテムはF6_FILEからF6_IDヘ変更します。
データのロードを行うページは、いくつかの処理でページの送信(フォームのPOST処理)を行います。ページの送信後ページが再表示される際に、セッション・ステートの保持がリクエストごと(メモリーのみ)であると、変更されたページ・アイテムが保持されず初期状態に戻ります。モーダル・ダイアログなど、ページの送信後に別ページに遷移する場合は、送信時のページ・アイテムの値を保持する必要はありませんが、今回は送信時のページを再表示するため、ページ・アイテムの値を保持するように設定します。
ページ・アイテムP6_CONTENTのソース定義を確認した後、削除します。すでに存在するP6_FILEをP6_CONTENTに変更します。
ページ・アイテムP6_FILEを選択し、名前をP6_CONTENTに変更します。設定の記憶域タイプをBLOB column specified in Item Source attributeに変更し、ソースに定義されているBLOBカラムに保存するよう指定します。MIMEタイプ列はCONTENT_MIMETYPE、ファイル名列はCONTENT_FILENAME、文字セット列はCONTENT_CHARSET、BLOB最終更新列はCONTENT_LASTUPDを指定し、ダウンロード・リンクの表示はOFFにします。
ソースの定義として、フォーム・リージョンにファイルのアップロードを選択します。列にCONTENTを選択し、データ型はBLOB、セッション・ステートの保持はセッションごと(ディスク)を選択します。
ページ・アイテム名は内部的にはIDにて保存されており(選択リストでページ・アイテムを設定するプロパティの場合)、P6_FILEとして指定されていたプロパティは改めて設定を変更しなくてもP6_CONTENTとして認識されるようになります。
ページ・アイテムP6_CONTNT_FILENAMEのソース定義を確認した後、削除します。すでに存在するP6_FILE_NAMEをP6_CONTENT_FILENAMEに変更します。
ページ・アイテムP6_FILE_NAMEを選択し、名前をP6_CONTENT_FILENAMEに変更します。ソースの定義として、フォーム・リージョンにファイルのアップロードを選択します。列にCONTENT_FILENAMEを選択し、データ型はVARCHAR2、セッション・ステートの保持はセッションごと(ディスク)を選択します。
ページ・アイテムP6_CONTENT_MIMETYPE、P6_CONTENT_CHARSET、P6_CONTENT_LASTUPDを非表示とするため、これらのページ・アイテムをすべて選択し、サーバー側の条件のタイプをなしとします。
リージョンの表示条件としてP6_FILE(名称をP6_FILEからP6_CONTENTに変更済みなので、P6_CONTENTとして表示される)がNULLかどうかで判断している部分をページ・アイテムP6_IDの有無で判定するように変更します。
タイプがファイル参照...のページ・アイテムで記憶域タイプとしてBLOB column specified in Item Source attributeが指定されていると、ページの送信後にセッション・ステートの保持の設定に関わらず、値が初期化される模様です。ユーザー表に保存する場合は、主キーによってデータの有無を判断する方が適切です。
リージョンのボタン・バーを選択し、サーバー側の条件のアイテムをP6_CONTENTからP6_IDヘ変更します。
サブ・リージョンのロード済みファイルに、同様の変更を行います。
リージョンのプレビューにも、同様の変更を行います。
ファイル・タイプの検証に使用されるコードを更新します。以下のコードでファイルがXLSXであることを検証します。
begin
if apex_data_parser.assert_file_type(
p_file_name => :P6_CONTENT,
p_file_type => apex_data_parser.c_file_type_xlsx )
then
return true;
else
apex_util.clear_page_cache;
return false;
end if;
end;
ページ・アイテムP6_CONTENTに紐づく検証Is valid file typeを選択し、PL/SQLファンクション本体を上記のコードに置き換えます。またサーバー側の条件として、タイプがアイテムはNULLではない、アイテムとしてP6_CONTENTを指定し、P6_CONTENTが指定されているときのみ検証を実施します。
動的アクションファイルのアップロードのTrueアクションページの送信を選択し、リクエスト/ボタン名にCREATEを設定します。プロセスフォームの行の自動処理(DML)は、このリクエスト/ボタン名を認識し行の挿入処理(つまりBLOBへの保存)を行います。
ページ・アイテムP6_CONTENT_FILENAMEに紐づく計算は不要なので削除します。(これから追加する)プロセスによって、ファイル名は設定されます。
アップロードされたExcelのシートを選択するためのページ・アイテムP6_XLSX_WORKSHEETの、LOVを表示するためのSQL問合せを以下に変更します。
select p.sheet_display_name,
p.sheet_file_name
from fdl_files f,
table( apex_data_parser.get_xlsx_worksheets( p_content => f.content ) ) p
where f.id = :P6_ID
サーバー側の条件として設定されているPL/SQLファンクション本体も以下のコードに変更します。Excelに含まれているシート数が1より多いときのみ、シートが選択を行います。
declare
l_sheet_count number;
begin
select count(*)
into l_sheet_count
from fdl_files f,
table( apex_data_parser.get_xlsx_worksheets( p_content => f.content ) ) p
where f.id = :P6_ID;
-- display if the XSLX file contains multiple worksheets
return ( l_sheet_count > 1 );
exception
when others then
return false;
end;
動的アクションSubmit worksheet on changeを選択し、名前をRefresh preview on changeに変更します。
識別のアクションをリフレッシュに変更し、影響を受ける要素の選択タイプとしてリージョンを選択します。リージョンとしてプレビューを指定します。
プレビューのソースであるSQL問合せを以下に変更します。
select p.line_number,
p.col001, p.col002, p.col003, p.col004, p.col005, p.col006, p.col007, p.col008, p.col009, p.col010
-- add more columns (col011 to col300) here.
from fdl_files f,
table( apex_data_parser.parse(
p_content => f.content,
p_file_name => f.content_filename,
p_xlsx_sheet_name => case when :P6_XLSX_WORKSHEET is not null then :P6_XLSX_WORKSHEET end,
p_add_headers_row => 'Y',
p_file_charset => 'AL32UTF8',
p_max_rows => 10 ) ) p
where f.id = :P6_ID
リージョンのプレビューを選択し、ソースのSQL問合せを上記のコードに更新します。リフレッシュの際にSELECT文で参照しているページ・アイテムの値が送信されるよう、送信するページ・アイテムとしてP6_IDおよびP6_XLSX_WORKSHEETを設定します。
左ペインでプロセス・ビューを開き、新規にファイルを表FDL_FILESに保存するプロセスを作成します。
プロセスの作成を行い、名前をファイルのアップロードとします。タイプにフォーム - 行の自動処理(DML)を選択し、フォーム・リージョンとしてファイルのアップロードを指定します。他の設定はデフォルトのままとします。
ユーザー表からのデータのロードについては、2021年5月15日の時点で不具合のためソース・データ型としてSQL Queryを使えません。そのため以下のPL/SQLコードにてデータのロードを行います。
declare
l_file blob;
l_res apex_data_loading.t_data_load_result;
begin
select content into l_file from fdl_files where id = :P6_ID;
l_res := apex_data_loading.load_data
(
p_static_id => 'CITYLIST',
p_data_to_load => l_file,
p_xlsx_sheet_name => case when :P6_XLSX_WORKSHEET is not null then :P6_XLSX_WORKSHEET end
);
:P6_ERROR_ROW_COUNT := l_res.error_rows;
end;
プロセスのデータのロードを選択し、タイプをコードを実行に変更し、上記のPL/SQLコードを設定します。
以上でアプリケーションの変更は完了です。
複数のシートを含むテスト用のExcelシートcitylist2.xlsxを以下に配置しました。
https://github.com/ujnak/apexapps/blob/master/exports/citylist2.xlsx
こちらを使用して、アプリケーションの動作を確認してみます。成功時のメッセージ出力などは調整の余地がありますが、概ねページ作成ウィザードによって作成されたページと同様な動作を行なっています。
今回作成したアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/apex-data-loading-sample.sql
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完