Wednesday, October 07, 2015

About downloading files from an Apex page without using download links

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

Yesterday I needed to download a file as the final step in processing a user request involving a tabular form. For some reason this requirement is somewhat unusual if you look at the most typical scenarios where you either download files stored in a table by means of report links or a static link where the developer can leverage Apex's built-in features for this purpose.
The main difference with my need lies in that there is no file at all to download until the user has selected one or more checkboxes in a tabular form and I need to process the tabular form after the submit in order to create the file.
In a case like this you cannot simply put the file download logic inside an after submit process because the browser will reload the page after the submit, so the file download request won't be caught by the browser. An alternative would be to create the file and then reload the page showing the download link somewhere, but I preferred to avoid the unnecessary page reload.

Luckily enough I stumbled upon Joel Kallman's posting about the more generic "file downloading" requirement and more specifically a comment he made gave me this idea.

As Joel says one can invoke the file download by means of an on-demand process that can be triggered from an Apex standard URL, so what if I try to set up the parameters in the branch section as needed?

The simple answer is: it can be done and it works well, the browser won't reload the page but it will begin the file download instead.

configuring a branch to trigger a file download request

The prerequisites for the branch are as follows:
  • a DOWNLOAD button performing a normal page submit.
  • a process that handles the tabular form data and gathers the rows involved, creating a file and storing it temporarily or permanently in a table of your own choosing, running conditionally on the pressing of the DOWNLOAD button.
  • An application item called FILEID containing the key to retrieve the file from the table above or alternatively a page item if the file download application process doesn't need to be invoked elsewhere.
  • An application process named DOWNLOAD that is invoked as shown above which reads the blob from the table using the provided key value.
A final note about this method: as I am storing the file(s) in a table I wanted to delete them automatically right away once the download was finished.
Initially I thought it would be smart to delete the relevant row from the table after the call to WPG_DOCLOAD.DOWNLOAD_FILE but to my surprise rows are not deleted from the table even if I explicitly commit the operation just after the delete statement.

It looks like as if any code after WPG_DOCLOAD.DOWNLOAD_FILE doesn't get executed at all.

Failing to find a solution for this issue, I fell back to the simple workaround of deleting the rows older than 1 day when invoking the code for creating a new file, but one may opt for a batch procedure that does the housekeeping as well.

7 comments:

SC said...

Give your table a column "is_downloaded", update the row before "WPG_DOCLOAD.DOWNLOAD_FILE" and write a procedure or something else to delete all rows with "is_downloaded" = "true". Event: for example: Scheduler Job

Byte64 said...

Hi SC,
if I have to call the scheduler then I can skip the is_downloaded column altogether, it's enough to invoke the job passing the key as a parameter.

Thank you,
Flavio

Lev said...

If you can not delete record after WPG_DOCLOAD.DOWNLOAD_FILE than delete it before.

1) load clob (or blob) from your table into temp variable
2) delete record
3) call WPG_DOCLOAD.DOWNLOAD_FILE
...

By the way in APEX 5 I was not able to set Content-type in http header with
ordinary call owa_util.mime_header

I had to use:

htp.flush;
htp.init;
-- set up HTTP header
owa_util.mime_header
...
as recommended in
http://blog.iadvise.eu/2014/03/26/wpg_docload-download_file-mime-type-not-recognized-by-client/

Unfortunately, htp.flush and htp.init are nor documented.

Do you know better way to set Content-type?

Thanks,
Lev

Byte64 said...

Hi Lev,
good idea, I'll try with the temp variable.

Regarding the mime type, I'll check when I can, but I don't remember noticing anything strange.

Thank you
Flavio

Byte64 said...

Lev,
are you experiencing the problem with the MIME header only on Apex 5.0?

I made a test with an application I wrote on 4.2 with different files having various MIME headers and I cannot replicate the problem you describe, I can always set the correct MIME header without the need of invoking those extra APIs.

I wondering if it could depend on some specific configuration/architecture that you might have in common with the people of iadvise.

Moreover I'd be curious to know how they managed to see the HTTP header of the file being downloaded in Firebug, I tried it and the file download action is not picked up at all in the "Net" panel of Firebug, it looks like it's completely transparent.

Flavio

Flavio Casetta said...

Lev,
I can confirm that on Apex 5.0 with ORDS as web server I don't see the issue of the MIME type, I am exporting TEXT/CSV files and Firefox is suggesting to open the file with Numbers, the spreadsheet application made by Apple.

There must be something else going on.

Flavio

Lev said...

Hi Flavio,

We had code that worked with Apex 4.0 for many years.
This code downloaded PDF documents.
Code was in Pre Rendering -> Before Header process

1) code fetched blob (pdf) from table
2) set mime type with owa_util.mime_header
3) call WPG_DOCLOAD.DOWNLOAD_FILE
4) stop apex engine

After upgrade to Apex 5.0
we changed step #4 and document was downloaded
correctly, but mime type was wrong whatever we tried.

Only after we added
htp.flush;
htp.init;

mime type was set properly.

I think that apex engine sets mime type before
our process that's why our call for
owa_util.mime_header didn't work.

Thanks,
Lev

yes you can!

Two great ways to help us out with a minimal effort. Click on the Google Plus +1 button above or...
We appreciate your support!

latest articles