Skip to Main Content
Feature Request FR-4678
Product Area Page Components
Status CLOSED

2 Voters

Add a Download in Background option into Interactive Grids and Reports

sjmackenzie40 Public
· Oct 2 2025

Idea Summary
To support asynchronous downloads of large files, that would otherwise cause problems or timeouts, add a “Download in Background” ACTION to the reports.  This would, via configuration, write the BLOB to a table in the parsing schema to support downloads at a later date.  The downloaded file SHOULD reflect the UI state of the report in terms of visible columns and manual filters applied to columns.

Use Case
To support asynchronous downloads of large files for later retrieval.

Preferred Solution (Optional)
Ideally I would add a “Download Background” action, and add some configuration which can be set to write the values into an Oracle table you specify, where you specify the column to store the EXECUTION_ID, APP_USER, FILE_NAME, FILE_SIZE, MIME_TYPE, EXECUTION_STATUS, ERROR etc.

When a user selects “Download Background”, an asynchronous job is started which takes into account the current state of the interactive grid, and writes the corresponding BLOB file and filename to the table specified in the configuration.  Users could then create a report based on this table to allow people to download the blobs at a later time, create jobs to clear down the blobs periodically and even add triggers to the table to send emails when the status of a download is complete.

This is a great idea! You can already achieve this in APEX today with a slightly different approach.

Comments

Comments

  • sjmackenzie40 OP 5 weeks ago

    Well I can’t work out how to run a background process to generate data that mirrors the UI state.  Is the alternative method as simple as ticking a box that says download in background?

  • carsten.czarski APEX Team OP 5 weeks ago

    You can do this today, by employing a bit of PL/SQL code.

    • The APEX_REGION.EXPORT_DATA function allows you to reference a region ( your IG region ), and it will perform a data export (as CSV, HTML, etc) taking the current report settings into account. You'll get the same as if you used Download in the Actions menu.
    • Store that CLOB into a table or a collection.
    • Kick that logic off using a page process, running in the background. The Background Page Process functionality provides functionality to …
      • See currently running executions in the APEX_APPL_PAGE_BG_PROC_STATUS view
      • Use the Return Execution ID into Item attribute of your Background Page Process (in Page Designer) to get the ID of your background activity into a page item.
    • Build a simple download page to show status of background executions and to provide ability to download your file.

    I don't quite see that as a declarative feature, as it implies so many parts, which have to be customized towards your application. While kicking off the actual background processing is rather simple, the steps after the file has been prepared are typically highly customized …

    • Some want to store it persistently for a while to allow multiple downloads, others want to have it stored only once and removed after first download, others want to send by email, others want to upload to Object Store and so on …
    • The execution status and download part will be a part of your application. Obviously, the UI for this is specific to your app - some want it to be a dedicated page, others want it to be a part of the IG page, others want a modal dialog.

    So, the best way to implement your requirement, is IMO indeed to use the existing building blocks, which are essentially APEX_REGION.EXPORT_DATA and the Background Page Processing functionality. The rest is then utilizing standard APEX functionality to build the UI for this as you need it.

  • sjmackenzie40 OP 5 weeks ago

    It may take the current report settings into account but not any live filtering, reordering or column hiding which has taken place in the client on top of the report definition.

  • carsten.czarski APEX Team OP 5 weeks ago

    Did you try it out already? 

    Interactive Grid Filters (also live filters) are taken into account. For Interactive Grid and Interactive Reports it's important to pick the right report ID, though. As soon as you do live filtering, you get a new report ID - and that will be the one you have to use. You need to examine contents of apex_appl_page_ig_rpts view.

  • sjmackenzie40 OP 5 weeks ago

    Without a complete working example, its a nightmare.  Hence my idea.  Lots of things are possible through the API, but you don't need to if it can be managed through configuration.  The clue is in the name “low code”

  • sjmackenzie40 OP 5 weeks ago
    DECLARE
        l_export       apex_data_export.t_export;
        l_region_id    NUMBER;
        l_report_id    NUMBER;
        l_current_exec apex_background_process.t_execution;
        l_execution_id NUMBER;
        l_file_name    VARCHAR2(255) := 'pvcompliance_icsr_intake_' || REPLACE(LOWER(:P14_INTAKE_ROUTE), ' ', '_') || '_' || TO_CHAR(SYSDATE, 'YYYYMMDD_HH24MISS') || '.csv';
        l_error_msg    VARCHAR2(4000);
    BEGIN
        -- Get the current background execution ID 
        l_current_exec := apex_background_process.get_current_execution;
        l_execution_id := l_current_exec.id;
    
        -- Insert initial row with RUNNING status (ensures row exists for updates)
        INSERT INTO apex_user_exports (execution_id, status, created_by, filename, num_rows)
        VALUES (l_execution_id, 'RUNNING', :APP_USER, l_file_name, :P14_NUM_ROWS);
    
        COMMIT;
    
        -- Get region ID using binds (ensure :APP_PAGE_ID is fully spelled)
        SELECT region_id
        INTO l_region_id
        FROM apex_application_page_regions
        WHERE application_id = :APP_ID
        AND page_id = :APP_PAGE_ID  -- Fixed: Full bind variable
        AND static_id = 'intake_region';
    
        -- Get the *current* report ID (captures last viewed saved report with its state/filters)
        l_report_id := APEX_IG.GET_LAST_VIEWED_REPORT_ID(
            p_page_id   => :APP_PAGE_ID,
            p_region_id => l_region_id  -- Fixed: Use NUMBER region_id, not static_id
        );
    
        -- Fallback to primary/default if no last viewed report
        IF l_report_id IS NULL THEN
            SELECT report_id
            INTO l_report_id
            FROM apex_appl_page_ig_rpts  -- Note: View name is case-sensitive in some envs
            WHERE application_id = :APP_ID
            AND page_id = :APP_PAGE_ID
            AND type = 'PRIMARY';  -- Fixed: Correct column for default report
        END IF;
    
        -- Export using current report ID (applies saved filters/sorts/etc.)
        l_export := apex_region.export_data(
            p_format      => apex_data_export.c_format_csv,
            p_page_id     => :APP_PAGE_ID,  -- Page ID (not app ID)
            p_component_id => l_report_id,  -- Now uses current/primary report
            p_as_clob     => FALSE,         -- For BLOB output
            p_region_id   => l_region_id
        );
    
        -- Update with BLOB and set to COMPLETE
        UPDATE apex_user_exports
        SET export_blob = l_export.content_blob,  -- Matches BLOB
            status = 'COMPLETE'
        WHERE execution_id = l_execution_id;
    
        COMMIT;  -- Commit the update
    
    EXCEPTION
        WHEN OTHERS THEN
            l_error_msg := SQLERRM;
            
            -- Log error (row should exist from initial INSERT)
            UPDATE apex_user_exports
            SET status = 'ERROR', 
                error_msg = l_error_msg
            WHERE execution_id = l_execution_id;
    
            COMMIT;  -- Commit error update
            
            RAISE;
    END;
    
  • sjmackenzie40 OP 5 weeks ago

    this last code returns 3,000 rows in the export despite the UI being filtered to 18.  When you can demonstrate a working example, I will accept the idea being cancelled.  it is supposed to be a low code framework, my suggestion is simply to add a “download in background” option, which reflects the live UI state, and lets you specify where the download should be stored.

  • carsten.czarski APEX Team OP 5 weeks ago

    Can we move this towards the forum?

    https://forums.oracle.com/ords/apexds/domain/dev-community/category/apex

    This is clearly now a how-to discussion, which we should continue there. Happy to help you getting this set up.

    -Carsten

  • sjmackenzie40 OP 5 weeks ago

    You can download a blob without a dynamic action, you still implemented that!! Its about making the product easier to use.

  • sjmackenzie40 OP 5 weeks ago

    AI says this is a nightmare, so if you can demonstrate it works, i will accept the idea being cancelled.  I would suggest you try and solve it yourself before cancelling a good idea.

  • carsten.czarski APEX Team OP 5 weeks ago

    I have an example app, which illustrated this, ready to share. However, attachments cannot be uploaded here. If you raise a question on the APEX discussion forum, I'm happy to share the app export.

    -Carsten

  • sjmackenzie40 OP 5 weeks ago

    Thanks I did, link is in above thread

  • carsten.czarski APEX Team OP 5 weeks ago

    An example app has been uploaded to the thread, which illustrates how this works today. However, there is indeed a bug hiding in APEX, which makes it harder to get the solution working - we are tracking this as bug 38525701.

  • pierre-antoine.madier OP 5 weeks ago

    It's really a shame that this functionality isn't natively available for reports. 

    It would avoid having to manually handle this situation on a large ERP system that contains hundreds of reports