APEXでCSVファイルをZIPしてダウンロードしてみた！

<h1>はじめに</h1>
APEXでINPUTデータを加工してから、OUTPUT(複数ビュー)をなどをcsvファイルにしてまとめてZip出来たらいいね！という話が合ったので、できるかどうかやってみました。
以下のblogを参考に作成しました。
https://www.apex-at-work.com/2015/10/custom-csv-export-as-zip-file.html
https://apexcornies.wordpress.com/2020/04/04/create-a-download-zip-button-in-oracle-apex/

<h1>事前準備</h1>
今回はTASKSとEMPLOYEEという表をcsvファイルに出力してZipでダウンロードしていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681023/85f15715-8301-35a7-ced7-f647b76301f0.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681023/65f6e3c0-953b-be61-0053-d25df555192a.png)

<h1>ボタンの作成</h1>
downloadボタンを作成します。
ボタン名を変更し、位置などは好きに配置してください。
基本はデフォルトのままで大丈夫です。
<img width="" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681023/465cb95f-bb40-3b7d-b090-383b9bad289f.png">

ひとまず、ZIPの横に配置
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681023/398013a5-3be4-0f02-c03f-47b5977ac37d.png">


<h1>ページプロセスの作成</h1>
プロセスを作成していきます。
左ペインの矢印のループになっているタブのプロセスを右クリックし、プロセスの作成をクリックします。
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681023/e960cf14-fd99-2820-c779-dfebe093e4cb.png">

プロセスを編集していきます。
名前にCreate_ZIPと付け、ソースの言語にをPL/SQLに、コードに以下のものを入力します。
ここでは、ZIPという名前のAPEX_COLLECTIONを作成し、ここにデータをcsv形式でBLOB化したものを追加していきます。
サーバー側の上条件で、ボタン押下時に先ほど作成したボタンを選択します。
<img width="200" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681023/b60f3036-169c-92d8-032d-518c5c99ac48.png"><img width="200" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681023/987ce172-65b9-3b3a-8513-f8b7a0c12a2c.png">


```PL/SQL
declare
    l_zip_file       blob;
    L_BLOB           BLOB;
    L_CLOB           CLOB;
    L_DEST_OFFSET    INTEGER := 1;
    L_SRC_OFFSET     INTEGER := 1;
    L_LANG_CONTEXT   INTEGER := DBMS_LOB.DEFAULT_LANG_CTX;
    L_WARNING        INTEGER;
    L_LENGTH         INTEGER;
    L_NAME           VARCHAR2(200);

cursor c1 IS
    select XMLCAST(XMLAGG(XMLELEMENT(E,COL_VALUE||CHR(13)||CHR(10))) AS CLOB) AS CLOB_VAL, 'TASKS' as TABLE_NAMES
    FROM  (
        SELECT 'ID,PROJECT,TASK_NAME,STATUS' AS COL_VALUE FROM DUAL
        UNION ALL
         SELECT ID||','||
            PROJECT||','||
            TASK_NAME||','||
            STATUS AS COL_VALUE  FROM TASKS)
    UNION ALL
    select XMLCAST(XMLAGG(XMLELEMENT(E,COL_VALUE||CHR(13)||CHR(10))) AS CLOB) AS CLOB_VAL, 'EMPLOYEE' as TABLE_NAMES
    from (
        SELECT 'ID,LOCATION,DEPARTMENT,NAME' AS COL_VALUE FROM DUAL
        UNION ALL
         SELECT ID||','||
            LOCATION||','||
            DEPARTMENT||','||
            NAME AS COL_VALUE  FROM EMPLOYEE)
    ;

begin    APEX_COLLECTION.CREATE_OR_TRUNCATE_COLLECTION(
        p_collection_name => 'ZIP');

	for rec in c1 loop
    DBMS_LOB.CREATETEMPORARY(L_BLOB, FALSE);

    L_DEST_OFFSET    := 1;
    L_SRC_OFFSET     := 1;
    L_LANG_CONTEXT   := DBMS_LOB.DEFAULT_LANG_CTX;
    L_WARNING        := null;

    DBMS_LOB.CONVERTTOBLOB( DEST_LOB     => L_BLOB,
                            SRC_CLOB     => rec.CLOB_VAL,
                            AMOUNT       => DBMS_LOB.LOBMAXSIZE,
                            DEST_OFFSET  => L_DEST_OFFSET,
                            SRC_OFFSET   => L_SRC_OFFSET,
                            BLOB_CSID    => NLS_CHARSET_ID('WE8MSWIN1252'),
                            LANG_CONTEXT => L_LANG_CONTEXT,
                            WARNING      => L_WARNING
                          );

    L_NAME := 'EXPORT_'||rec.table_names||'.csv';

    APEX_ZIP.ADD_FILE(
        p_zipped_blob => l_zip_file,
        p_file_name => L_NAME,
        p_content => L_BLOB
    );

    end loop;

/*---------------------------------------------------------*/

    apex_zip.finish (
        p_zipped_blob => l_zip_file );

    APEX_COLLECTION.ADD_MEMBER(
        p_collection_name => 'ZIP',
        p_blob001            => l_zip_file);


END;
```
CURSORの中のSQLでcsvで取得したいデータを指定します。
引数を渡せば、合致するものだけでファイルを作成することができます。

<h1>ブランチの作成</h1>
ブランチを作成していきます。
左ペインの矢印のループになっているタブのブランチを右クリックし、ブランチの作成をクリックします。
名前にGet Zipとつけ、動作のターゲットをZIPを行うページに設定してください。
<img width="300" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681023/b3871241-a38d-d564-c200-26ec021a6c8f.png"><img width="200" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681023/a6d3b4d9-8fcc-558e-e769-0a8b8b8c2372.png">
また、ターゲットの詳細でこの後作成するAjaxコールバックのプロセスの名前を指定します。
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681023/53dfdfeb-a5d0-e0d3-dc2d-af30190fb719.png">


<h1>Ajaxコールバックの作成</h1>
Ajaxコールバックを作成していきます。
左ペインの矢印のループになっているタブのAjaxコールバックを右クリックし、プロセスの作成をクリックします。
<img width="200" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681023/c92aa0a3-7d54-f8dd-ae5e-3c2e7e3e95c8.png">
名前にdownload_zip_fileと付け、PL/SQLのコードに以下のものを入れます。
<img width="200" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681023/63020fc8-d4b0-c8bb-17a9-4ba57618ad01.png">

```
declare
  var_mimetype varchar2(50) := 'application/zip';
  var_name varchar2(100) := 'files_by_project.zip';
  var_blob blob;
   
begin
    -- Get the BLOB from the ZIP collection
    select blob001 into var_blob from apex_collections where collection_name = 'ZIP' and seq_id = 1;
     
    sys.htp.init;
    sys.owa_util.mime_header( var_mimetype, FALSE );
    sys.htp.p('Content-length: ' || sys.dbms_lob.getlength( var_blob));
    sys.htp.p('Content-Disposition: attachment; filename="' || var_name || '"' );
    sys.htp.p('Cache-Control: max-age=3600');  -- tell the browser to cache for one hour, adjust as necessary
    sys.owa_util.http_header_close;
    sys.wpg_docload.download_file( var_blob );
 
    apex_application.stop_apex_engine;
     
exception when apex_application.e_stop_apex_engine then
    NULL;
     
end;
```
var_nameを変更すると、ダウンロードするZIPファイルの名前を変更できます。

<h1>実装結果</h1>
実際にdownloadしていきます！
files_by_project.zipというファイルがダウンロードできました。
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681023/a0b5d150-6015-0506-0109-7e145534d19c.png">

zipファイルの中身はこんな感じです。
無事にどちらのcsvファイルも取得できています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681023/3d59b91d-7739-4f4b-d4f1-fad402c9a99f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681023/c2b8634a-5d59-aba3-25b5-dbba1c91cc1f.png)


<h2>あとがき</h2>
できるかどうかはやってみたものの実際には開発しなさそうなので、こちらに。