如何在Java中用Apache POI-3.17读取未保存的实时更新Excel值
解决方案:通过COM自动化读取运行中Excel的实时数据
首先得明确:Apache POI 3.17本身是针对磁盘上的静态Excel文件进行读写的,而Nest Trader实时更新的是运行中Excel进程内存里的单元格数据——这些数据还没保存到磁盘,甚至Excel会独占锁定文件,导致POI根本无法读取到最新内容。所以我们得换个思路,直接从Excel的内存实例中读取数据,而不是读磁盘文件。
这里推荐用JACOB(Java-COM Bridge)实现Java和Windows COM接口的交互,直接调用Excel的ActiveX对象来获取实时数据。
步骤1:引入依赖
先给项目添加JACOB的依赖(Maven示例):
<dependency> <groupId>net.sf.jacob-project</groupId> <artifactId>jacob</artifactId> <version>1.18</version> </dependency>
注意:JACOB需要对应系统位数的dll文件(jacob-1.18-x86.dll或jacob-1.18-x64.dll),要把它放到JRE/bin目录下,或者通过VM参数指定路径。
步骤2:编写核心代码
下面是完整示例,实现毫秒级读取Excel实时值并存入数据库:
import com.jacob.activeX.ActiveXComponent; import com.jacob.com.Dispatch; import com.jacob.com.Variant; import java.util.Timer; import java.util.TimerTask; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; public class ExcelRealTimeReader { private ActiveXComponent excelApp; private Dispatch targetWorkbook; private Dispatch targetSheet; // 初始化:找到Nest Trader对应的Excel工作簿 public void init(String workbookName) { try { // 获取已运行的Excel实例 excelApp = new ActiveXComponent("Excel.Application"); excelApp.setProperty("Visible", new Variant(false)); // 无需显示Excel窗口 // 遍历所有打开的工作簿 Dispatch workbooks = excelApp.getProperty("Workbooks").toDispatch(); int workbookCount = Dispatch.call(workbooks, "Count").getInt(); for (int i = 1; i <= workbookCount; i++) { Dispatch workbook = Dispatch.call(workbooks, "Item", new Variant(i)).toDispatch(); String name = Dispatch.call(workbook, "Name").getString(); if (name.equals(workbookName)) { targetWorkbook = workbook; // 替换为你需要读取的工作表(这里取第一个) targetSheet = Dispatch.call(targetWorkbook, "Sheets", new Variant(1)).toDispatch(); break; } } if (targetWorkbook == null) { throw new RuntimeException("未找到目标工作簿:" + workbookName); } } catch (Exception e) { e.printStackTrace(); } } // 读取指定单元格的实时值(Excel行列从1开始计数) private String getCellValue(int row, int col) { if (targetSheet == null) return null; Dispatch cell = Dispatch.call(targetSheet, "Cells", new Variant(row), new Variant(col)).toDispatch(); return Dispatch.call(cell, "Value").getString(); } // 启动定时读取任务(毫秒级频率) public void startReading(int intervalMs, String dbUrl, String dbUser, String dbPwd) { Timer timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { try { // 替换为你需要读取的单元格位置 String latestPrice = getCellValue(1, 1); String latestVolume = getCellValue(1, 2); if (latestPrice != null && latestVolume != null) { saveToDatabase(latestPrice, latestVolume, dbUrl, dbUser, dbPwd); } } catch (Exception e) { e.printStackTrace(); } } }, 0, intervalMs); } // 数据库存储方法 private void saveToDatabase(String price, String volume, String dbUrl, String dbUser, String dbPwd) { String sql = "INSERT INTO stock_real_time_data (price, volume, update_time) VALUES (?, ?, NOW())"; try (Connection conn = DriverManager.getConnection(dbUrl, dbUser, dbPwd); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1, price); pstmt.setString(2, volume); pstmt.executeUpdate(); } catch (Exception e) { e.printStackTrace(); } } // 释放资源,避免Excel进程残留 public void destroy() { if (targetWorkbook != null) { Dispatch.call(targetWorkbook, "Close", new Variant(false)); // 不保存关闭 } if (excelApp != null) { excelApp.invoke("Quit", new Variant[0]); excelApp.safeRelease(); } } public static void main(String[] args) { ExcelRealTimeReader reader = new ExcelRealTimeReader(); try { // 替换为你的Nest Trader输出工作簿名称 reader.init("StockOutput.xlsx"); // 每100毫秒读取一次,替换为你的数据库连接信息 reader.startReading(100, "jdbc:mysql://localhost:3306/stock_db", "root", "your_password"); } catch (Exception e) { e.printStackTrace(); reader.destroy(); } } }
关键注意事项
- 运行环境限制:这个方案只能在Windows系统上运行,因为依赖Windows的COM技术。
- Excel状态:必须确保Nest Trader对应的Excel文件处于打开状态,且没有被其他进程锁定。
- 性能优化:如果毫秒级更新频率很高,建议改成批量插入(比如攒10条数据再一次性插入),避免频繁数据库操作拖慢性能。
- 异常处理:要处理Excel被意外关闭的情况,比如在定时任务中检测
targetWorkbook是否为空,为空则重新初始化获取实例。 - 资源释放:程序结束时一定要调用
destroy()方法,否则Excel进程会残留占用系统资源。
内容的提问来源于stack exchange,提问作者CH.Rajshekhar
相关产品推荐
相关产品推荐

