使用 LightCells API

事件驱动架构

Aspose.Cells 提供了LightCells API,主要是用来对cell数据进行逐个操作,而不需要将完整的数据模型块(使用Cell集合等)构建到内存中。它以事件驱动模式工作。

保存工作簿时,保存时逐格提供单元格内容,组件直接保存到输出文件中。

读取模板文件时,组件会解析每个单元格并一一提供它们的值。

在这两个过程中,一个 Cell 对象被处理然后被丢弃,Workbook 对象不保存集合。因此,在这种模式下,导入和导出具有大量数据集的 Microsoft Excel 文件时会节省内存,否则会占用大量内存。

即使 LightCells API 以相同的方式处理 XLSX 和 XLS 文件的单元格(它实际上并不将所有单元格加载到内存中,而是处理一个单元格然后丢弃它),它为 XLSX 文件比 XLS 文件更有效地节省内存,因为两种格式的不同数据模型和结构。

然而,对于 XLS 文件,为了节省更多的内存,开发者可以指定一个临时位置来保存Save过程中产生的临时数据。通常,使用 LightCells API 保存 XLSX 文件可以节省 50% 或更多内存比使用常见的方式,保存 XLS 可能会节省大约 20-40% 的内存.

编写大型 Excel 文件

Aspose.Cells提供了一个接口LightCellsDataProvider,需要在你的程序中实现。该接口表示用于以轻量级模式保存大型电子表格文件的数据提供程序。

使用此模式保存工作簿时,会在保存工作簿中的每个工作表时检查 startSheet(int)。对于一张工作表,如果 startSheet(int) 为真,则此工作表要保存的所有行和单元格的数据和属性均由此实现提供。首先,调用 nextRow() 以获取要保存的下一行索引。如果返回有效的行索引(行索引必须按升序排列才能保存行),则提供表示该行的 Row 对象供实现,以通过 startRow(Row) 设置其属性。

对于一行,首先检查 nextCell()。如果返回有效的列索引(列索引必须按升序排列才能保存一行的所有单元格),则提供一个代表该单元格的 Cell 对象,以通过 startCell(Cell) 设置数据和属性。设置好该单元格的数据后,直接将该单元格保存到生成的电子表格文件中,对下一个单元格进行检查处理。

以下示例显示了 LightCells API 的工作原理。

以下程序在工作表中创建了一个包含 100,000 条记录的巨大文件,其中充满了数据。我们向工作表中的某些单元格添加了一些超链接、字符串值、数值和公式。此外,我们还格式化了一系列单元格。

// For complete examples and data files, please go to https://github.com/aspose-cells/Aspose.Cells-for-Java
public class LightCellsDataProviderDemo implements LightCellsDataProvider {
private final int sheetCount;
private final int maxRowIndex;
private final int maxColIndex;
private int rowIndex;
private int colIndex;
private final Style style1;
private final Style style2;
public LightCellsDataProviderDemo(Workbook wb, int sheetCount, int rowCount, int colCount) {
// set the variables/objects
this.sheetCount = sheetCount;
this.maxRowIndex = rowCount - 1;
this.maxColIndex = colCount - 1;
// add new style object with specific formattings
style1 = wb.createStyle();
Font font = style1.getFont();
font.setName("MS Sans Serif");
font.setSize(10);
font.setBold(true);
font.setItalic(true);
font.setUnderline(FontUnderlineType.SINGLE);
font.setColor(Color.fromArgb(0xffff0000));
style1.setHorizontalAlignment(TextAlignmentType.CENTER);
// create another style
style2 = wb.createStyle();
style2.setCustom("#,##0.00");
font = style2.getFont();
font.setName("Copperplate Gothic Bold");
font.setSize(8);
style2.setPattern(BackgroundType.SOLID);
style2.setForegroundColor(Color.fromArgb(0xff0000ff));
style2.setBorder(BorderType.TOP_BORDER, CellBorderType.THICK, Color.getBlack());
style2.setVerticalAlignment(TextAlignmentType.CENTER);
}
public boolean isGatherString() {
return false;
}
public int nextCell() {
if (colIndex < maxColIndex) {
colIndex++;
return colIndex;
}
return -1;
}
public int nextRow() {
if (rowIndex < maxRowIndex) {
rowIndex++;
colIndex = -1; // reset column index
if (rowIndex % 1000 == 0) {
System.out.println("Row " + rowIndex);
}
return rowIndex;
}
return -1;
}
public void startCell(Cell cell) {
if (rowIndex % 50 == 0 && (colIndex == 0 || colIndex == 3)) {
// do not change the content of hyperlink.
return;
}
if (colIndex < 10) {
cell.putValue("test_" + rowIndex + "_" + colIndex);
cell.setStyle(style1);
} else {
if (colIndex == 19) {
cell.setFormula("=Rand() + test!L1");
} else {
cell.putValue(rowIndex * colIndex);
}
cell.setStyle(style2);
}
}
public void startRow(Row row) {
row.setHeight(25);
}
public boolean startSheet(int sheetIndex) {
if (sheetIndex < sheetCount) {
// reset row/column index
rowIndex = -1;
colIndex = -1;
return true;
}
return false;
}
}
// For complete examples and data files, please go to https://github.com/aspose-cells/Aspose.Cells-for-Java
public class Demo {
private static final String OUTPUT_FILE_PATH = Utils.getDataDir(LightCellsDataProviderDemo.class);
public static void main(String[] args) throws Exception {
// Instantiate a new Workbook
Workbook wb = new Workbook();
// set the sheet count
int sheetCount = 1;
// set the number of rows for the big matrix
int rowCount = 100000;
// specify the worksheet
for (int k = 0; k < sheetCount; k++) {
Worksheet sheet = null;
if (k == 0) {
sheet = wb.getWorksheets().get(k);
sheet.setName("test");
} else {
int sheetIndex = wb.getWorksheets().add();
sheet = wb.getWorksheets().get(sheetIndex);
sheet.setName("test" + sheetIndex);
}
Cells cells = sheet.getCells();
// set the columns width
for (int j = 0; j < 15; j++) {
cells.setColumnWidth(j, 15);
}
// traverse the columns for adding hyperlinks and merging
for (int i = 0; i < rowCount; i++) {
// The first 10 columns
for (int j = 0; j < 10; j++) {
if (j % 3 == 0) {
cells.merge(i, j, 1, 2, false, false);
}
if (i % 50 == 0) {
if (j == 0) {
sheet.getHyperlinks().add(i, j, 1, 1, "test!A1");
} else if (j == 3) {
sheet.getHyperlinks().add(i, j, 1, 1, "http://www.google.com");
}
}
}
// The second 10 columns
for (int j = 10; j < 20; j++) {
if (j == 12) {
cells.merge(i, j, 1, 3, false, false);
}
}
}
}
// Create an object with respect to LightCells data provider
LightCellsDataProviderDemo dataProvider = new LightCellsDataProviderDemo(wb, 1, rowCount, 20);
// Specify the XLSX file's Save options
OoxmlSaveOptions opt = new OoxmlSaveOptions();
// Set the data provider for the file
opt.setLightCellsDataProvider(dataProvider);
// Save the big file
wb.save(OUTPUT_FILE_PATH + "/DemoTest.xlsx", opt);
}
}

读取大型 Excel 文件

Aspose.Cells 提供一个接口,LightCellsDataHandler,需要在你的程序中实现。该接口表示用于以轻量级模式读取大型电子表格文件的数据提供程序。

以这种模式读取工作簿时,会在读取工作簿中的每个工作表时检查 startSheet()。对于工作表,如果 startSheet() 返回 true,则检查并处理工作表的行和列中单元格的所有数据和属性。对于每一行,都会调用 startRow() 来检查它是否需要处理。如果需要处理一行,则首先读取该行的属性,开发人员可以使用 processRow() 访问其属性。

如果该行的单元格也需要处理,则 processRow() 返回 true 并为该行中的每个现有单元格调用 startCell() 以检查它是否需要处理。如果是,则调用 processCell()。

下面的示例代码说明了这个过程。该程序读取一个包含数百万条记录的大文件。阅读工作簿中的每张纸需要一点时间。示例代码读取文件并检索每个工作表的单元格总数、字符串计数和公式计数。

// For complete examples and data files, please go to https://github.com/aspose-cells/Aspose.Cells-for-Java
public class LightCellsTest1 {
public static void main(String[] args) throws Exception {
String dataDir = Utils.getDataDir(LightCellsTest1.class);
LoadOptions opts = new LoadOptions();
LightCellsDataHandlerVisitCells v = new LightCellsDataHandlerVisitCells();
opts.setLightCellsDataHandler((LightCellsDataHandler) v);
Workbook wb = new Workbook(dataDir + "LargeBook1.xlsx", opts);
int sheetCount = wb.getWorksheets().getCount();
System.out.println("Total sheets: " + sheetCount + ", cells: " + v.cellCount + ", strings: " + v.stringCount
+ ", formulas: " + v.formulaCount);
}
}

实现 LightCellsDataHandler 接口的类

// For complete examples and data files, please go to https://github.com/aspose-cells/Aspose.Cells-for-Java
public class LightCellsDataHandlerVisitCells implements LightCellsDataHandler {
public int cellCount;
public int formulaCount;
public int stringCount;
public LightCellsDataHandlerVisitCells() {
this.cellCount = 0;
this.formulaCount = 0;
this.stringCount = 0;
}
public int cellCount() {
return cellCount;
}
public int formulaCount() {
return formulaCount;
}
public int stringCount() {
return stringCount;
}
public boolean startSheet(Worksheet sheet) {
System.out.println("Processing sheet[" + sheet.getName() + "]");
return true;
}
public boolean startRow(int rowIndex) {
return true;
}
public boolean processRow(Row row) {
return true;
}
public boolean startCell(int column) {
return true;
}
public boolean processCell(Cell cell) {
this.cellCount = this.cellCount + 1;
if (cell.isFormula()) {
this.formulaCount = this.formulaCount + 1;
} else if (cell.getType() == CellValueType.IS_STRING) {
this.stringCount = this.stringCount + 1;
}
return false;
}
}