2021年4月21日水曜日

Excelファイルのアップロード - その1- 表APEX_APPLICATION_TEMP_FILESを使う方法

 以下のようなExcelファイルのアップロードを行う実装を行ってみます。Excelファイルのファイル名はcitylist.xlsxとします。


その1として、
表APEX_APPLICATION_TEMP_FILESを使う方法
その2として、
作成済みの表のBLOB列を使う方法
の2つの方法に取り組みます。

最初であるこの記事では、表APEX_APPLICATION_TEMP_FILESを使った方法を実装します。

ファイルのアップロードを行うアプリケーションは、いままでに何件か記事を書いています。今回は、Oracle APEX 21.1で実装される予定のデータ・ロードの機能を手作業で作ることで、その実装を理解することを目的としています。

アップロードする表は、以下のクイックSQLのモデルによって作成します。

# prefix: fup
# semantics: default
citylist
prefecture vc80
city vc80
count num

実際に実行するDDLとしては以下になります。

create table fup_citylist (
id number generated by default on null as identity
constraint fup_citylist_id_pk primary key,
prefecture varchar2(80),
city varchar2(80),
count number
)
;

ファイルのアップロードを実装するために、空のアプリケーションを作成します。アプリケーションの名前ファイルのアップロードとします。アプリケーションの作成を実行します。

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

最初にOracle APEXが提供している表APEX_APPLICATION_TEMP_FILESに一旦ファイルをアップロードしたのち、対象の表FUP_CITYLISTヘデータの投入を行うページを作成します。

静的コンテンツのリージョンを含むページを作成します。ページの作成を実行し、ページ作成ウィザードを開始します。

空白ページをクリックします。


名前APEX標準表ページ・モード標準オプションの静的コンテンツ・リージョンとして、リージョンをひとつ、リージョン1としてファイルのアップロードを作成します。に進みます。


ナビゲーションのプリファレンスとして、新規ナビゲーション・メニュー・エントリの作成を選択します。に進みます。


終了をクリックすると、静的コンテンツのリージョンがひとつあるページが作成されます。


ページが作成されたら、アップロードするファイルを選択するページ・アイテムを作成します。識別名前P2_FILEタイプファイル参照...を指定します。ラベルとしてアップロードするファイル表示形式として今回はBlock Dropzoneにしてみます。表示形式だけなので、どれを選んでもファイルは選択できます。ドロップ・ゾーンの説明としてExcelファイル(XLSX形式)をドロップしてください。と記述します。記憶域タイプとしてTable APEX_APPLICATION_TEMP_FILESを選択します。ファイルをパージするタイミングEnd of Sessionとします。最大ファイル・サイズとして5000KBの制限を与えています。


一旦ファイルが選択されたら、その後にページ・アイテムが変更されることを防ぐため、サーバー側の条件タイプアイテムがNULLを選択し、アイテムP2_FILEを指定します。この設定によりファイルが未選択のときに限り、ファイル参照が表示されます。


ファイルが選択された時点で表APEX_APPLICATION_TEMP_FILESに保存されるよう、動的アクションを作成します。ページ・アイテムP2_FILE上でコンテキスト・メニューを表示させ、動的アクションの作成を実行します。

動的アクションの名前ファイルのアップロードとします。タイミングはデフォルトでイベント変更選択タイプアイテムアイテムP2_FILEとなり、P2_FILEが変更されるとアクションが実行されます。


Trueアクションを選択し、アクションとしてページの送信を選択します。


この時点でファイルのアップロードまでは実装できています。

アップロードされた結果を確認するために、アップロードされたファイルのファイル名を表示するページ・アイテムを追加します。

ページ・アイテムを作成し、名前P2_FILE_NAMEタイプ表示のみラベルファイル名とします。


ファイルのアップロード後、表APEX_APPLICATION_TEMP_FILESから登録されたファイル名を取り出し、ページ・アイテムP2_FILE_NAMEに設定します。ページ・アイテムP2_FILE_NAMEに計算の作成を行います。


実行オプションポイント送信後(送信後に計算を行うという意味)を選択します。計算タイプとしてSQL問合せ(単一の値を返す)を選択し、以下のSQL問合せを設定します。

select filename
from apex_application_temp_files
where name = :P2_FILE


以上でアップロードされたファイルのファイル名が、ページ・アイテムP2_FILE_NAMEに表示されます。さらに、アップロードされたデータをプレビューします。

クラシック・レポートのリージョンを追加します。

リージョンを作成し、名前プレビュータイプクラシック・レポートとします。ソース位置ローカル・データベースタイプSQL問合せとし、以下のSELECT文を記載します。

select
col001 "PREFECTURE",
col002 "CITY",
col003 "COUNT"
from
apex_data_parser.parse(
p_content =>
(
select blob_content from apex_application_temp_files
where name = :P2_FILE
),
p_file_name => :P2_FILE_NAME,
p_skip_rows => 1,
p_file_charset => 'AL32UTF8',
p_max_rows => 10
)


プレビューに使用したSELECT文ですが、この後に、このSELECT文の結果を表FUP_CITYLISTに挿入する処理を追加します。

ここまでの動作は以下のようになります。


表FUD_CITYLISTにデータを投入するプロセスを作成します。データの送信を行うボタンを作成します。ボタン名B_SUBMITラベル送信とします。


続いて、ボタンB_SUBMITが押された時に実行されるプロセスを作成します。名前表にロードとし、タイプコードを実行ソース位置ローカル・データベース言語PL/SQLを選択し、PL/SQLコードとして以下を記述します(プレビューではないのでp_max_rowsの指定を除いています)。サーバー側の条件として、ボタン押下時B_SUBMITを指定することにより、送信ボタンを押した時のみ実行されるようにします。

begin
delete from fup_citylist;
insert into fup_citylist(prefecture, city, count)
select
col001 "PREFECTURE",
col002 "CITY",
col003 "COUNT"
from
apex_data_parser.parse(
p_content =>
(
select blob_content from apex_application_temp_files
where name = :P2_FILE
),
p_file_name => :P2_FILE_NAME,
p_skip_rows => 1,
p_file_charset => 'AL32UTF8'
);
end;


上記のコードは、送信をクリックした際に、すでに保存されているデータをすべて削除し、新たにアップロードしたデータで入れ替えます。追加やマージといった動作にしたい場合は、PL/SQLのコードを変更することになります。

以上で表へのデータのロードも実行されるようになりました。動作確認をもう少し簡単にするため、ページを初期化するボタンと表FUD_CITYLISTの内容を表示するレポートを追加します。

ボタンの作成を行い、ボタン名B_CLEARラベルクリアとします。


ボタンB_CLEARを押した時に実行されるプロセスを作成します。作成したプロセスの名前ページの初期化とし、タイプセッション・ステートのクリアを選択します。サーバー側の条件として、ボタン押下時B_CLEARを選択します。これでクリアのボタンを押した時に、ページが初期化されます。


アップロード結果を表示するクラシック・レポートを作成します。

名前アップロード結果とし、タイプクラシック・レポートを選択します。ソース表名FUP_CITYLISTを選択し、プレビューと横並びになるよう、レイアウト新規行の開始OFFとします。


以上で完成です。調整すべきところはありますが(例えばプレビューにORA-20987のエラーが発生するなど)、当初の目的は達成しています。

ページを実行し、実装を確認します。以下の動作になります。


作成済みの表のBLOB列を使う方法は、次の記事として記載します。