I recently received a challenge to find a way to download multiple files at once in APEX. The requirements were that I couldn't use a zip file, and I couldn't use javascript. I found an approach that works in APEX 5.1, but it relies on creating a loop using the dynamic action "Refresh". I don't know if this is a feature or a bug, so I'm not sure if this approach is stable. Does this approach currently work in APEX 19.1? And if so, is it likely to stop working in the near future?
The approach I took:
Start by taking an existing page in your application or creating a new page.
Add the following items to the page:
An LOV whose values can be used to determine which files should be downloaded. For the proof of concept, I created a list of the numbers 1-10 in Shared Components, and accessed this list from a shuttle titled “P1_LOV”.
A hidden item to contain the list of all files left to be downloaded. For this demonstration, I titled it “P1_DOWNLOAD_LIST”.
A hidden item to contain the current file to be downloaded. For this demonstration, I titled it “P1_CURRENT_DOWNLOAD”.
A button to start the download process. For this demonstration, I titled the button “P1_DOWNLOAD”.
----------
Add the following branches to the page:
A branch back to this page, we’ll name this branch “Go to Download”
- Point = “After Submit”
- Behavior:
- Type = “Page or URL (Redirect)”
- Target = page 1 (current page)
- Request = “DOWNLOAD” (set in the Advanced section of the Link Builder pop-up. This request will trigger the next branch, “Download File”)
- Server-side Condition:
- Type = “Request = Value”
- Value = “DOWNLOAD” (the requests don’t have to have the same value, but I think it makes things easier)
A branch to call the actual download procedure, we’ll call this branch “Download File”
- Point = “Before Header”
- Behavior
- Type = “PL/SQL Procedure”
- PL/SQL Code = your_download_procedure_here(:P1_CURRENT_DOWNLOAD);
- Server-side Condition
- Type = “Request = Value”
- Value = “DOWNLOAD” (This will cause it to be triggered by the "Go to Download" branch)
----------
Add the following dynamic actions to the page:
A dynamic action to start the loop for the download process when the user clicks the Download button.
- Event = “Click”
- Selection Type = “Button”
- Button = “P1_DOWNLOAD”
- True actions:
- Set Value
- Set Type = “PL/SQL Expression”
- PL/SQL Expression = “:P1_LOV”
- Items to Submit = P1_LOV
- Affected Elements: P1_DOWNLOAD_LIST
- Refresh
- Affected Elements: P1_LOV (This will trigger the next dynamic action)
A dynamic action to run the loop. It is essential that this action is triggered by the LOV item, as the Refresh action only works for items with cascading LOV support.
- Event = “After Refresh”
- Selection Type = “Item(s)”
- Item(s) = P1_LOV
- Client-side Condition: “Item is not null” for Item = P1_DOWNLOAD_LIST
- True actions:
- Set Value
- Set Type = “PL/SQL Expression”
- PL/SQL Expression = “REGEXP_SUBSTR(:P1_DOWNLOAD_LIST, '([^:])(:?.*)', 1, 1, 'i', 1)” (Gets the first value in the colon-delimited list)
- Items to Submit = P1_DOWNLOAD_LIST
- Item(s) = P1_CURRENT_DOWNLOAD
- Submit Page (Triggers the “Go to Download Page” branch)
- Request / Button Name = “DOWNLOAD”
- Show Processing = No
- Set Value
- Set Type = “PL/SQL Expression”
- PL/SQL Expression = “REGEXP_SUBSTR(:P1_DOWNLOAD_LIST, '(:)(.*)', 1, 1, 'i', 2)” (Removes the first value from the list)
- Items to Submit = P1_DOWNLOAD_LIST
- Item(s) = P1_DOWNLOAD_LIST
- Refresh
- Selection Type = “Item(s)”
- Item(s) = P1_LOV
Overhead view:
To download items from this page, select any number of elements from the shuttle, move them to the right side of the shuttle, and then click the download button.
Upon clicking the Download button, the application will set the value of a hidden item P1_DOWNLOAD_LIST to be a colon-delimited list of the items to be downloaded, and then start a loop that will remove items from the list and download them until P1_DOWNLOAD_LIST is empty.
On each iteration of the loop, the application will
- Take the first item on the list.
- Copy it to the hidden item P1_CURRENT_DOWNLOAD.
- Submit the page so it branches to the download procedure (thus initiating the download of the item in P1_CURRENT_DOWNLOAD).
- Set the value of P1_DOWNLOAD_LIST so it no longer contains the item that was just downloaded.