将 POI 工作簿流式传输到 servlet 输出流

问题描述 投票:0回答:6

我在我的网络服务器上构建了一个非常大的 POI 工作簿。将整个工作簿保存在内存中,不会针对多个并发请求进行扩展。有没有办法可以逐步将工作簿写入 servlet 输出流。这应该会减少响应时间,并提高进程内存效率。

java servlets apache-poi
6个回答
10
投票

如果您要生成 Excel 2007 (xslx),那么您可以采用 BigGridDemo.java 的方法,如下所述: http://web.archive.org/web/20110821054135/http://www.developrealers.com /博客/代码/excel

解决方案是让 POI 生成一个容器 xslx 仅作为模板,并将实际电子表格数据作为 XML 流式传输到 zip 输出流中。简化 XML 生成就取决于您了。


8
投票

自从写完其余答案以来,情况已经有了很大改善 - Streaming 现在是 Apache Poi 的一部分。

请参阅 SXSSFWorkbook 类,以及 此处的文档。它在工作表上使用流式窗口,将窗口外的旧行刷新到临时文件。

这是基于

hlg 的答案
中使用的 BigGridDemo 方法,但现在是官方发行版的一部分。

这是文档中的示例:

public static void main(String[] args) throws Throwable {
    // keep 100 rows in memory, exceeding rows will be flushed to disk
    SXSSFWorkbook wb = new SXSSFWorkbook(100); 
    Sheet sh = wb.createSheet();
    for(int rownum = 0; rownum < 1000; rownum++){
        Row row = sh.createRow(rownum);
        for(int cellnum = 0; cellnum < 10; cellnum++){
            Cell cell = row.createCell(cellnum);
            String address = new CellReference(cell).formatAsString();
            cell.setCellValue(address);
        }

    }

    // Rows with rownum < 900 are flushed and not accessible
    for(int rownum = 0; rownum < 900; rownum++){
      Assert.assertNull(sh.getRow(rownum));
    }

    // ther last 100 rows are still in memory
    for(int rownum = 900; rownum < 1000; rownum++){
        Assert.assertNotNull(sh.getRow(rownum));
    }

    FileOutputStream out = new FileOutputStream("/temp/sxssf.xlsx");
    wb.write(out);
    out.close();

    // dispose of temporary files backing this workbook on disk
    wb.dispose();
}

3
投票

不幸的是,当没有顺序数据时这是不可能的。我建议寻找另一种格式,例如CSV 或 XML。两者都可以按顺序写出。如果它来自数据库,甚至可以更有效地完成,因为一个像样的数据库具有内置设施可以有效地导出到这些格式。您只需将字节从一侧流式传输到另一侧即可。


1
投票

您是否尝试过直接使用 HttpServletResponse.getOutputStream() 的 write 方法?

请看下面的例子:

 HSSFWorkbook wb = new HSSFWorkbook();
 HSSFSheet sheet = wb.createSheet("new sheet");
 ...
 OutputStream out = response.getOutputStream();
 wb.write(out);
 out.close();

0
投票

如果您使用JExcel 它具有从 Servlet 读取流代码和从 Servlet 读取流代码的示例代码。 http://jexcelapi.sourceforge.net/resources/faq/

此 API 的唯一缺点是它仅支持 Excel 2003(含)。

使用 POI - 您不能创建文件并将文件的字节提供给 servlet 输出流吗?


0
投票

从版本 5.0.0 开始,有一个 DeferredSXSSFWorkBook 类可用,它使用生成器函数来处理行。这样您就可以直接流式传输到您的 HTTP 响应。我从 DeferredGeneration.java 示例中获取了下面的代码。该 API 被认为是实验性的。在我的用例中它运行良好。

// ExcelWriter.java
public class ExcelWriter {
  public static void write(OutputStream out) throws IOException {
    try (DeferredSXSSFWorkbook wb = new DeferredSXSSFWorkbook()) {
      DeferredSXSSFSheet sheet1 = wb.createSheet("new sheet");

      // cell styles should be created outside the row generator function
      CellStyle cellStyle = wb.createCellStyle();
      cellStyle.setAlignment(HorizontalAlignment.CENTER);

      sheet1.setRowGenerator((ssxSheet) -> {
          for (int i = 0; i < 10; i++) {
              Row row = ssxSheet.createRow(i);
              Cell cell = row.createCell(1);
              cell.setCellStyle(cellStyle);
              cell.setCellValue("value " + i);
          }
      });

      // wb.write(out);
      // writeAvoidingTempFiles was added as an experimental change in POI 5.1.0
      wb.writeAvoidingTempFiles(out);

      // the dispose call is necessary to ensure temp files are removed
      // supposedly writeAvoidingTempFiles doesn't guarantee that temp files are NOT created
      wb.dispose();
    }
  }
}

这就是我在 Quarkus 3.8.3 w/ RestEasy Reactive 中实现它的方式。请注意,这仍然是一个阻塞调用。

// SomeResource.java
@GET
@Path("my-endpoint/xlsx")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@ResponseHeader(name="content-disposition", value="attachment; filename=file.xlsx")
public StreamingOutput getFile() {
 
  return new StreamingOutput() {
    @Override
    public void write(OutputStream out) throws IOException, WebApplicationException {
      ExcelWriter.write(out);
    }
  };
}

© www.soinside.com 2019 - 2024. All rights reserved.