You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何后台运行PL/SQL实现Excel导入进度条并动态设置duration?

实现APEX中Excel导入的实时进度条

核心思路

后台PL/SQL无法直接向前端推送进度,所以需要通过会话级存储保存进度值,前端定时轮询获取进度后调用插件更新。同时根据剩余工作量动态计算进度条的duration参数,让动画更贴合实际处理节奏。


步骤1:存储进度状态

利用APEX的会话级应用项来保存进度(比临时表更简单,自动绑定当前会话):

  1. 在应用中创建两个应用项:
    • P20_TOTAL_ROWS:存储待导入的总行数
    • P20_CURRENT_ROWS:存储已处理的行数

步骤2:PL/SQL导入逻辑中更新进度

在你的apex_data_parser导入循环里,定期更新进度值(避免每行都更新,减少性能开销):

DECLARE
    l_total_rows NUMBER;
    l_current_row NUMBER := 0;
    -- 替换为你的解析数据集
    CURSOR c_parsed_data IS
        SELECT * FROM TABLE(apex_data_parser.parse(
            p_content => :P20_EXCEL_FILE, -- 上传的Excel文件项
            p_file_name => :P20_FILE_NAME
        ));
BEGIN
    -- 先获取总行数
    SELECT COUNT(*) INTO l_total_rows FROM TABLE(apex_data_parser.parse(
        p_content => :P20_EXCEL_FILE,
        p_file_name => :P20_FILE_NAME
    ));

    -- 初始化进度
    apex_util.set_session_state('P20_TOTAL_ROWS', l_total_rows);
    apex_util.set_session_state('P20_CURRENT_ROWS', 0);

    -- 逐行处理并插入数据
    FOR rec IN c_parsed_data LOOP
        l_current_row := l_current_row + 1;
        
        -- 替换为你的目标表插入逻辑
        INSERT INTO your_target_table (col1, col2, col3)
        VALUES (rec.col1, rec.col2, rec.col3);

        -- 每处理100行更新一次进度(可根据数据量调整间隔)
        IF MOD(l_current_row, 100) = 0 THEN
            apex_util.set_session_state('P20_CURRENT_ROWS', l_current_row);
        END IF;
    END LOOP;

    -- 最后更新为100%
    apex_util.set_session_state('P20_CURRENT_ROWS', l_total_rows);
END;

步骤3:创建AJAX回调获取进度

在APEX中创建一个AJAX回调进程(命名为GET_PROGRESS),用于前端获取当前进度:

DECLARE
    l_current_rows NUMBER := apex_util.get_session_state('P20_CURRENT_ROWS');
BEGIN
    apex_json.open_object();
    apex_json.write('current_rows', l_current_rows);
    apex_json.close_object();
END;

步骤4:前端轮询与进度条更新

在页面中添加JavaScript逻辑,实现定时轮询进度并动态更新进度条:

function startProgressPolling() {
    const progressBar = apexProgressBar('P20_PROGRESS_BAR');
    const totalRows = parseInt(apex.item('P20_TOTAL_ROWS').getValue());
    let pollingInterval;

    // 初始化进度条
    progressBar.setValues({percentage: 0, message: "开始导入...", duration: 0});

    // 每500ms轮询一次进度
    pollingInterval = setInterval(() => {
        apex.server.process('GET_PROGRESS', {}, {
            success: function(data) {
                const currentRows = parseInt(data.current_rows);
                
                if (currentRows >= totalRows) {
                    // 导入完成,终止轮询
                    progressBar.setValues({percentage: 100, message: "导入完成!", duration: 1000});
                    clearInterval(pollingInterval);
                    apex.message.showPageSuccess('Excel数据导入成功!');
                } else {
                    // 计算当前进度百分比
                    const percentage = Math.round((currentRows / totalRows) * 100);
                    
                    // 动态计算duration:根据剩余行数预估处理时间
                    // 示例:假设每行处理耗时10ms(可根据实际测试调整)
                    const estimatedMsPerRow = 10;
                    const remainingMs = (totalRows - currentRows) * estimatedMsPerRow;
                    // 限制duration范围,避免动画过短或过长
                    const duration = Math.max(500, Math.min(5000, remainingMs));

                    // 更新进度条
                    progressBar.setValues({
                        percentage: percentage,
                        message: `已处理 ${currentRows}/${totalRows} 行`,
                        duration: duration
                    });
                }
            },
            error: function() {
                clearInterval(pollingInterval);
                progressBar.setValues({percentage: 0, message: "获取进度失败", duration: 1000});
                apex.message.showPageError('导入过程中出现错误,请检查日志!');
            }
        });
    }, 500);
}

// 绑定到导入按钮的点击事件
document.getElementById('P20_IMPORT_BUTTON').addEventListener('click', function() {
    // 先清空之前的进度状态
    apex.item('P20_CURRENT_ROWS').setValue(0);
    
    // 启动进度轮询
    startProgressPolling();

    // 异步调用导入进程(需创建名为IMPORT_EXCEL的AJAX回调,内容为步骤2的PL/SQL代码)
    apex.server.process('IMPORT_EXCEL', {}, {
        error: function() {
            apex.message.showPageError('导入失败,请检查Excel格式或数据!');
        }
    });
});

关键细节说明

  1. 进度更新频率:不要每行都更新进度,建议每处理100~1000行更新一次,平衡实时性和性能。
  2. duration动态计算:
    • 可以通过测试预估单条数据的处理时间(比如导入1000条耗时10秒,则每条10ms)
    • 剩余时间=(总行数-已处理行数)×单条耗时,再限制duration在500~5000ms之间,避免动画异常
  3. 异步执行:导入进程必须通过AJAX异步调用,否则页面会被阻塞,无法进行轮询。

内容的提问来源于stack exchange,提问作者user22817983

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 13:43:20