ラベル ダウンロード の投稿を表示しています。 すべての投稿を表示
ラベル ダウンロード の投稿を表示しています。 すべての投稿を表示

2025年3月13日木曜日

レポート上にダウンロード・ボタンを配置しZIP圧縮してコンテンツをダウンロードする

対話モード・レポート上にダウンロード・ボタンを配置し、ボタンのある行のBLOB列のコンテンツをZIP圧縮してダウンロードします。無圧縮であればレポート列の設定だけでコンテンツのダウンロードはできます。今回はZIP圧縮するため、Oracle APEX 24.1で追加された動的アクションダウンロードを活用します。

わかりにくい例ですが、以下のように動作するAPEXアプリケーションを作成します。


最初に標準のダウンロードを実装したAPEXアプリケーションを作成します。

SQLワークショップクイックSQLを開き、以下のモデルを元に表EBAJ_PHOTOSを作成します。
# prefix: ebaj
# pk: identity
photos
    title vc80 /nn
    photo file

画面に従ってDDLの生成と実行を行います。表が作成されたら、アプリケーションの作成を呼び出します。


作成されるアプリケーションに、デフォルトで表EBAJ_PHOTOSフォーム付き対話モード・レポートのページが含まれます。

そのまま、アプリケーションを作成します。


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

最初に対話モード・レポートのページを修正します。ページ・デザイナでページ番号を開きます。


対話モード・レポートの列PHOTOを選択します。ページ作成ウィザードBLOB属性の一部を設定しません。

MIMEタイプ列PHOTO_MIMETYPEファイル名列PHOTO_FILENAME最終更新列PHOTO_LASTUPD文字セット列PHOTO_CHARSETを設定します。

以上で変更を保存します。


ページ・デザイナでページ番号のフォームのページを開き、同様の変更を行います。

ページ・アイテムP3_PHOTOを選択し、ストレージMIMEタイプ列PHOTO_MIMETYPEファイル名列PHOTO_FILENAME文字セット列PHOTO_CHARSETBLOB最終更新列PHOTO_LASTUPDを設定します。

以上で変更を保存します。


以上で、APEXアプリケーションに標準のファイル・アップロードとダウンロードが実装されました。

作成したアプリケーションを実行し、いくつか写真をアップロードします。

対話モード・レポートには、写真をダウンロードするリンクが表示されます。標準のダウンロードは、アップロードした写真がそのままダウンロードされます。


これから、レポート上のダウンロードのリンクをボタンに変更し、写真がZIP圧縮されてダウンロードされるように機能を加えます。

ページ・デザイナで対話モード・レポートのページを開きます。

最初にダウンロード対象の写真を持つ行のIDを保持するページ・アイテムを作成します。

識別名前P2_IDタイプ非表示とします。動的アクションにより値を設定するため、設定保護された値オフにします。セッション・ステートストレージリクエストごと(メモリーのみ)です。


JavaScriptから参照できるように、対話モード・レポート静的IDとしてREPORTを設定します。


PHOTOをボタンに変更します。

識別タイププレーン・テキストに変更し、列の書式HTML式に以下を記述します。カスタム属性data-idに行の主キー値#ID#を渡しています。

<button type="button" class="t-Button" data-id="#ID#">Download</button>

この列もJavaScriptから参照できるように、静的IDとしてCOLUMN_DOWNLOADを設定します。


ユニバーサル・テーマのリファレンスに含まれるボタン・ビルダーを使用すると、ボタンのマークアップを生成できます。

https://apex.oracle.com/pls/apex/r/apex_pm/ut/button-builder


ボタンをクリックしたときに、ページ・アイテムP2_IDに主キーの値を設定する動的アクションを作成します。

左ペインで動的アクション・ビューを開き、クリックのノードに動的アクションを作成します。

識別名前onClick Download on Reportとします。実行イベント有効範囲動的に変更し、静的コンテナ(jQueryセレクタ)に対話モード・レポートの静的IDである#REPORTを設定します。

タイミングイベントクリック選択タイプjQueryセレクタを選び、jQueryセレクタに以下を記述します。列PHOTOのセルにあるボタンを指定しています。

td[headers="COLUMN_DOWNLOAD"] button


TRUEアクションとして値の設定を実行します。

設定タイプの設定JavaScript式を選択し、JavaScript式として以下を記述します。クリックしたボタンのカスタム属性data-idに渡している値を参照しています。

this.triggeringElement.dataset.id

ページ・アイテムで発生する変更イベントを受けてダウンロードを実行するため、変更イベントの禁止オフにします。値の設定先となる影響を受ける要素選択タイプアイテムアイテムP2_IDを選択します。

実行初期化時に実行オフです。


ページ・アイテムP2_ID動的アクションを作成します。

識別名前onChange IDタイミングイベント変更です。ページ・アイテムP2_IDの値が変更されたときに動的アクションが実行されます。


TRUEアクションとしてダウンロードを実行します。

設定複数のファイルオンにし、ダウンロードされるファイルがZIP圧縮されるようにします。ファイル名photo-&P2_ID..zipとします。

ダウンロードされるZIPファイルに含まれるファイルは、ソースに設定します。

SQL問合せとして以下を記述します。送信するアイテムとしてP2_IDを設定します。

select photo, photo_filename from ebaj_photos where id = :P2_ID

実行初期化時に実行オフにします。


以上で実装は完了です。アプリケーションを実行すると、記事に先頭にあるGIF動画のように動作します。

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

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

2024年7月25日木曜日

APEX 24.1で追加されたファイルのダウンロードを行なうプロセスと動的アクションを使ってみる

Oracle APEX 24.1では動的アクションタイプとしてダウンロードが追加されました。また、プロセスタイプとしてもダウンロードが追加されています。

データベースに保存されているBLOBおよびCLOBをダウンロードするために、WPG_DOCLOAD.DOWNLOAD_FILEAPEX_DATA_EXPORT.DOWNLOADを呼び出すコードを書く必要がなくなりました。また、動的アクションからファイルのダウンロードを呼び出すこともできるため、Ajaxコールバックを作成する必要も無くなりました。

BLOBやCLOBのダウンロードに関して、これからはコードを書く必要はありません。

実際に新しく追加された動的アクションのダウンロードとプロセスのダウンロードを使ったアプリケーションを作成してみます。

作成したアプリケーションは以下のように動作します。


最初にファイルをBLOBとして保存する表EBMJ_TEMP_FILESを作成します。以下のDDLを実行します。
create table ebmj_temp_files (
    id                  number generated by default on null as identity
                        constraint ebmj_temp_files_id_pk primary key,
    content             blob,
    content_filename    varchar2(255 char),
    content_mimetype    varchar2(255 char),
    content_charset     varchar2(255 char),
    content_lastupd     date
);

空のアプリケーションを作成します。名前新しいダウンロードとします。


アプリケーションが作成されます。すべての機能はホーム・ページに実装します。


最初にアップロードするファイルを複数選択するページ・アイテムを作成します。

識別名前P1_UPLOAD_FILESタイプファイルのアップロードラベルUpload Filesとします。ストレージタイプとして表APEX_APPLICATION_TEMP_FILESを選択します。

アップロードしたファイルは表EBMJ_TEMP_FILESに保存しますが、ストレージタイプアイテム・ソース属性で指定されたBLOB列を選択すると単一ファイルの選択に制限されるため、一旦、APEX_APPLICATION_TEMP_FILESにアップロードした後にEBMJ_TEMP_FILESに移し替えます。

ファイルをパージするタイミングリクエストの終わり複数ファイルの許可オンにします。

セッション・ステートストレージリクエストごと(メモリーのみ)を設定します。


選択したファイルをアップロードするボタンを作成します。

識別名前UPLOADラベルUploadとします。外観ホットオンテンプレート・オプションWidthStretchとします。

動作アクションはデフォルトのファイルの送信です。処理中を表示オンにします。この設定はOracle APEX 24.1で新規に追加されました。ページの処理中にページ全体をグレーアウトし、他の操作を抑止します。


アップロードされたファイルを表EBMJ_TEMP_FILESに入れ替えるプロセスを作成します。

識別名前Store Uploaded Filesタイプコードを実行です。ソースPL/SQLコードとして以下を記述します。
insert into ebmj_temp_files(content_filename, content_mimetype, content, content_lastupd)
select filename, mime_type, blob_content, created_on
from apex_application_temp_files
where name in (select column_value from apex_string.split(:P1_UPLOAD_FILES, ':'));
サーバー側の条件ボタン押下時UPLOADを指定します。


アップロードされたファイルを表示するリージョンを作成します。リージョンのタイプとしてContent Rowを選択し、列の複数選択を有効にします。そのため、選択した列の情報を保存するページ・アイテムを先に作成します。

識別名前P1_SELECTED_FILESタイプテキスト・フィールドとします。一般的な実装ではタイプ非表示にしますが、今回はサンプルなので値を確認しやすいように内容を画面に表示します。

セッション・ステートストレージリクエストごと(メモリーのみ)です。


アップロードされたファイルを一覧するリージョンを作成します。

識別名前FilesタイプContent Rowとします。表名としてEBMJ_TEMP_FILESを選択します。


選択された行は、主キーの値がページ・アイテムP1_SELECTED_FILESに設定されます。

IDソース主キーオンであることを確認します。


リージョンの属性を開き、表示のされ方を設定します。

外観表示複数(レポート)です。設定Titleとして&CONTENT_FILENAME.を設定し、ファイル名を表示させます。Display Avatarオンにします。

AvatorTypeImageを選択します。(そのため、アップロードされたファイルがExcelなどの画像でない場合はAvatorが表示されません。)Imageをクリックし、メディア属性ソースとしてBLOB列を選択し、表EBMJ_TEMP_FILESの列を指定します。SizeExtra Largeを選択します。

行選択タイプ複数選択とし、現在の選択のページ・アイテムとしてP1_SELECTED_FILESを指定します。


Imageメディア属性では、ソースとしてBLOB列を選択します。BLOB列CONTENTファイル名列CONTENT_FILENAMEMIMEタイプ列CONTENT_MIMETYPE最終更新列CONTENT_LASTUPDを指定します。


以上で表EBMJ_TEMP_FILESにファイルをアップロードできるようになりました。

アプリケーションを実行し、いくつかファイルをアップロードします。

Upload Filesをクリックし、いくつかファイルを選択した後にボタンUploadをクリックします。アップロードしたファイルが表示されたら、いくつか行を選択してみます。


これで、ファイルをダウンロードする機能を実装する準備ができました。

これから複数のボタンを作成します。それらのボタンを配置するリージョンを作成します。

リージョンを作成しページ・アイテムP1_SELECTED_FILESの下に配置します。

識別名前Buttonsタイプ静的コンテンツとします。外観テンプレートButtons Containerを選択します。


テストを容易にするため、選択したファイルを削除するボタンを作成します。ボタンを配置するリージョンButtonsです。

識別ボタン名DELETEラベルDelete動作アクションページの送信とします。

処理中を表示オン確認の要求オン確認メッセージ本当に削除しますか?として、スタイル危険を選択します。


実際にファイルを削除するプロセスを作成します。

識別名前Delete Filesタイプコードを実行です。

ソースPL/SQLコードとして以下を記述します。
delete from ebmj_temp_files where id in (select column_value from apex_string.split(:P1_SELECTED_FILES, ':'));
サーバー側の条件ボタン押下時DELETEを指定します。


これで簡単にダウンロードの対象とするファイルを入れ替えられるようになりました。

これからAPEX 24.1の新機能を使ったファイルのダウンロードを実装します。

複数のファイルをZIPにまとめてダウンロードするボタンを作成します。動的アクションにて実装します。

識別ボタン名DOWNLOAD_MULTIラベルDownload (Multi)とします。ボタンを横一列に並べるため、レイアウト新規行の開始オフにします。

動作アクションとして動的アクションで定義を選択します。


ボタンDOWNLOAD_MULTI動的アクションを作成します。

識別名前onClick DOWNLOAD_MULTIとします。タイミングイベントはボタンのデフォルトであるクリックです。


TRUEアクションとしてAPEX 24.1で新設されたダウンロードを選択します。

このボタンのアクションでは、複数のファイルをひとつのZIPファイルとしてダウンロードします。

複数のファイルオンにします。ファイル名としてdownload_&SYSDATE_YYYYMMDD..zipを設定します。一般的にAutonomous Databaseの場合、 データベースのタイムゾーンはUTCとなっているため&SYSDATE_YYYYMMDD.はUTCで置き換わります。ファイル名に置換文字列を含める例としてAPEX標準のSYSDATE_YYYYMMDDを含めています。

ソースSQL問合せとして以下を記述します。検索されたBLOBがZIPファイルに含まれます。
select content, content_filename from ebmj_temp_files 
where id in (select column_value from apex_string.split(:P1_SELECTED_FILES, ':'))
送信するアイテムとしてP1_SELECTED_FILESを指定します。

これでデータベースにBLOBとして保存されているファイルを、ひとつのZIPファイルにまとめてダウンロードできるようになりました。


選択したファイルをブラウザに表示するボタンを作成します。動的アクションにて実装します。

識別ボタン名VIEW_SINGLEラベルView (Single)とします。ボタンの設定や動的アクションの作成手順は、ボタンDOWNLOAD_MULTIと同様です。動的アクション名前onClick VIEW_SINGLEとします。


TRUEアクションダウンロードを選び、設定複数のファイルオフファイルの表示形式インラインを選択します。

インラインを選ぶと、ファイルはブラウザに表示されます。ファイルの表示形式として添付を選ぶと、通常のファイルとしてのダウンロードになります。

ソースSQL問合せとして以下を記述します。検索結果が複数ある場合は、先頭行が表示/ダウンロードの対象となる仕様になっていますが、fetch句を付けて検索結果が1行になるように制限しています。
select content, content_filename from ebmj_temp_files 
where id in (select column_value from apex_string.split(:P1_SELECTED_FILES, ':'))
fetch first 1 rows only
送信するアイテムとしてP1_SELECTED_FILESを指定します。

これでデータベースにBLOBとして保存されているファイルを、ブラウザに表示できるようになりました。


APEX 24.1では動的アイテムのアクション以外に、プロセスタイプとしてもダウンロードが追加されています。ただし、プロセスによるダウンロードは用途が限定的です。直リンクによるアクセスによりファイルをダウンロードするような場合が、主な用途になると思います。

レンダリング前ヘッダーの前に、複数のファイルをまとめてZIPとしてダウンロードするプロセスを作成します。ダウンロードのプロセスはレンダリング前に作成する必要があります。

識別名前Download Filesタイプダウンロードとします。

設定複数のファイルオンファイル名download_&SYSDATE_YYYYMMDD..zipソースSQL問合せとして以下を記述します。
select content, content_filename from ebmj_temp_files 
where id in (select column_value from apex_string.split(:P1_SELECTED_FILES, ':'))
サーバー側の条件タイプとしてリクエスト = 値を選択し、DOWNLOADを指定します。


ファイルの選択時に参照しているページ・アイテムP1_SELECTED_FILESは、セッション・ステートに保存されている値か、ページ呼び出し時の引数として渡された値を参照します。

このプロセスを呼び出すボタンを作成します。

識別ボタン名DOWNLOAD_PROCESSラベルDownload (Process)とします。レイアウト新規行の開始オフです。動作アクションはデフォルトのページの送信とします。


レンダリング前に配置されているプロセスを呼び出すだけであれば、動作アクションとしてこのアプリケーションのページにリダイレクトを選択し、ターゲットとして以下を指定することにより、ダウンロードのプロセスを呼び出すことができます。

しかし、アイテムの設定で名前P1_SELECTED_FILESとして渡している\&P1_SELECTED_FILES.\(値は:(コロン)で分割されているためバックスラッシュによるエスケープは必須です)は、ページが最初に表示されたときの値で置き換えられるため、ページ表示後にユーザーが行った選択は反映されません。

そのため、このボタンでは一旦ページの送信を行い、ブランチからダウンロードのプロセスを呼び出すようにします。また、ページの送信処理が終了する前にブランチにより別のページに遷移するため、処理が終わっていないと見做されます。そのため、処理中を表示オフにします。

詳細リクエストとしてDOWNLOADを設定し、ダウンロードのプロセスの実行条件とします。


プロセスの後に、ボタンDOWNLOAD_PROCESSのクリックにより実行されるブランチを作成します。

識別名前Call Download動作タイプページまたはURL(リダイレクト)ターゲットは前出の設定になります。

サーバー側の条件ボタン押下時DOWNLOAD_PROCESSを指定します。


ターゲット成功メッセージオンオフどちらでも成功メッセージは表示されません。
  1. そもそも、ブランチに先行して実行されるプロセスが無いため、表示する成功メッセージ自体がありません。
  2. このブランチダウンロードのプロセスを呼び出します。ダウンロードのプロセスは、ダウンロードが完了した時点でAPEXのページ・プロセスを終了するため、ページのレンダリングは行われません。そのため、仮に成功メッセージがあっても、成功メッセージは表示されません。

以上で、ボタンDOWNLOAD_PROCESSのクリックからブランチが呼び出され、ファイルのダウンロードが行われるようになりました。

レンダリング前に、選択したファイルをインラインで表示するプロセスを作成します。ボタンVIEW_SINGLEの動的アクションの設定と、ほぼ同じです。

識別名前View Fileタイプダウンロード設定複数のファイルオフとし、ファイルの表示形式インラインを選択します。

ソースSQL問合せとして以下を記述します。
select content, content_filename from ebmj_temp_files 
where id in (select column_value from apex_string.split(:P1_SELECTED_FILES, ':'))
fetch first 1 rows only
サーバー側の条件タイプとしてリクエスト = 値を選択し、VIEWを指定します。


このプロセスを呼び出すボタンブランチを作成します。

ボタンDOWNLOAD_PROCESSとブランチCall Downloadとほぼ同じ設定になります。

作成するボタン識別ボタン名VIEW_PROCESSラベルView (Process)とします。その他はボタンDOWNLOAD_PROCESSと同じです。


作成するブランチ識別名前Call Viewサーバー側の条件ボタン押下時VIEW_PROCESSを指定します。


ターゲット詳細リクエストとしてVIEWを設定します。それ以外はブランチCall Downloadと同じ設定です。


以上で、ボタンVIEW_PROCESSのクリックからブランチが呼び出され、ファイルの表示が行われるようになりました。

今回の実装は以上で完了です。アプリケーションを実行すると、記事の先頭のGIF動画のように動作します。

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

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

2024年2月9日金曜日

APEX_REGION.OPEN_QUERY_CONTEXT呼び出し時にp_outer_sqlを指定して出力する列を選択する

APEXのPL/SQL APIとして提供されているAPEX_REGION.OPEN_QUERY_CONTEXTの引数のひとつにp_outer_sqlがあります。p_outer_sqlにリージョンのデータ・ソースを囲む形で記述したSELECT文を与えることにより、APEX_REGION.OPEN_QUERY_CONTEXTの出力結果を置き換えることができます。

このp_outer_sqlの使い方のひとつを、以前に記事「ファセットによる検索結果の合計や平均を計算する」で紹介しています。ファセットを適用した検索結果の列SALおよびCOMMの合計、平均、最小値、最大値、件数を別のレポートに表示しています。この例ではAPEX_REGION.OPEN_QUERYに引数p_outer_sqlを指定して、その結果をパイプライン表関数とする、少々複雑な実装を行っています。

本記事では引数p_outer_sqlの、もう少し簡単な使用例を紹介します。こちらの記事「ファセット検索の検索結果をAPEX_REGION.OPEN_QUERY_CONTEXTを呼び出してCSV形式でダウンロードする」にて、ファセット検索の結果をCSVでダウンロードする実装を紹介しています。このダウンロード処理に、引数p_outer_sqlを使って以下の機能を追加します。
  1. CSVへ出力する列を選択する。
  2. 列MGRとDEPTNOの出力を名前ではなく、生データの数値にする。
出力列を選択するページ・アイテムをP3_PRINT_COLUMNSとして作成します。


ページ・アイテムP3_PRINT_COLUMNSには、以下の設定を行います。

タイプチェックボックス・グループです。ラベルPrint Columnsとしていますが、外観テンプレートHiddenを選択しているため表示はされません。レイアウトラベル列スパンにして、テンプレート・オプションStretch From Itemチェックします。列の数10としているため(列の数は8)、選択する列は横一列で、画面の左右いっぱいに表示されます。

セッション・ステートストレージセッションごと(永続)を選びます。ダウンロード処理を実行するAjaxコールバックのプロセスに引数を渡すことができないため、選択した列を保存しているページ・アイテムP3_PRINT_COLUMNSの値は、変更が発生したときにセッション・ステートに保存します。Ajaxコールバックセッション・ステートに保存されたページ・アイテムP3_PRINT_COLUMNSの値を参照します。


LOVタイプ静的値として、表示値戻り値を設定します。


戻り値は必ずレポート列の列名にします。


ページ・アイテムP3_PRINT_COLUMNSに動的アクションを作成します。タイミングイベントとして変更を選択します。


TRUEアクションとしてサーバー側のコードを実行を選択します。送信するアイテムP3_PRINT_COLUMNSを指定し、この動的アクションが呼び出された時に、画面上のページ・アイテムP3_PRINT_COLUMNSの値をデータベースに送信します。データベースは受信した値をセッション・ステートに保存します。ページ・アイテムの値をセッション・ステートに保存するだけであれば、PL/SQLコードの実行は不要です。PL/SQLコードを空にはできないため、null;を記述します。


CSVのダウンロードを実行するPL/SQLコードに、引数p_outer_sqlの指定を追加します。



p_outer_sqlに以下を渡し、出力する列としてEMPNOEmployee NameJobManagerDepartmentを選択してCSVダウンロードを実行します。チェックボックス・グループで複数選択された値は':'(コロン)で区切られています。SELECT文の列として認識されるために','(カンマ)に置き換えています。

l_outer_sql := 'select ' || replace(:P3_PRINT_COLUMS,':',',') || ' from #APEX$SOURCE_DATA#';

置換文字列#APEX$SOURCE_DATA#は、指定したリージョン自体の問い合わせに置き換えられます。この結果セットは、レポート・リージョンに定義された列を持ちます。


以下のCSVファイルがダウンロードされます。


p_outer_sqlに以下を渡すと、列MGRDEPTNOが数値になります。

l_outer_sql := 'select ' || replace(:P3_PRINT_COLUMS,':',',') || ' from emp where empno in (select empno from #APEX$SOURCE_DATA#)';


APEX_REGION.OPEN_QUERY_CONTEXTの引数p_outer_sqlの使い方の紹介は以上になります。

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