如何后台运行PL/SQL实现Excel导入进度条并动态设置duration?
实现APEX中Excel导入的实时进度条
核心思路
后台PL/SQL无法直接向前端推送进度,所以需要通过会话级存储保存进度值,前端定时轮询获取进度后调用插件更新。同时根据剩余工作量动态计算进度条的duration参数,让动画更贴合实际处理节奏。
步骤1:存储进度状态
利用APEX的会话级应用项来保存进度(比临时表更简单,自动绑定当前会话):
- 在应用中创建两个应用项:
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格式或数据!'); } }); });
关键细节说明
- 进度更新频率:不要每行都更新进度,建议每处理100~1000行更新一次,平衡实时性和性能。
- duration动态计算:
- 可以通过测试预估单条数据的处理时间(比如导入1000条耗时10秒,则每条10ms)
- 剩余时间=(总行数-已处理行数)×单条耗时,再限制
duration在500~5000ms之间,避免动画异常
- 异步执行:导入进程必须通过AJAX异步调用,否则页面会被阻塞,无法进行轮询。
内容的提问来源于stack exchange,提问作者user22817983
相关产品推荐
相关产品推荐

