我正在尝试使用SXSSFWorkbook从头开始编写Excel电子表格。
wb = SXSSFWorkbook(500)
wb.isCompressTempFiles = true
sh = streamingWorkbook.createSheet(t.getMessage("template.sheet.name"))
一切都很好,但是当我调用最终代码时:
val out = FileOutputStream(localPath)
wb.write(out)
out.close()
// dispose of temporary files backing this workbook on disk
wb.dispose()
我得到了一个巨大的Excel文件,而不是我期望的压缩XLSX。我尝试手动压缩文件,并且可以将文件从120MB压缩到9MB。那我想念什么?
使用:Kotlin和
implementation group: 'org.apache.poi', name: 'poi-ooxml', version: '4.1.2' // For `.xlsx` files
-更新1
我的印象是xlsx是本质上包含XML数据的压缩文件[1]。通过XSSFWorkbook和SXSSFWorkbook输出的POI至少可以压缩10个数量级。我已使用此简单代码演示:
fun main() {
val workbook = XSSFWorkbook()
writeRowsAndSave(workbook, "test.xlsx")
workbook.close()
val streamingWorkbook = SXSSFWorkbook(IN_MEMORY_ROWS_WINDOW_SIZE)
streamingWorkbook.isCompressTempFiles = true
writeRowsAndSave(streamingWorkbook, "test-streaming.xlsx")
streamingWorkbook.dispose()
}
private fun writeRowsAndSave(workbook: Workbook, fileName: String) {
val ROWS_COUNT = 2_000
val COLS_COUNT = 1_000
val sheet = workbook.createSheet("Test Sheet 1")
for (i in 1..ROWS_COUNT) {
val row = sheet.createRow(i)
println("Row $i")
for(j in 1..COLS_COUNT) {
row.createCell(j).setCellValue("Test $i")
}
}
FileOutputStream("./$fileName").use {
workbook.write(it)
}
}
这将产生5MB的文件,每个文件在压缩后大约有439KB(?!)。
SXSSFWorkbook
默认使用内联字符串而不是共享字符串表。这意味着SXSSFWorkbook
即使在同一文本中多次写入文本,也直接在工作表中写入文本。 XSSFWorkbook
和Excel的GUI都使用共享字符串表,其中文本获取索引,同一文本仅存储一次,然后在工作表中使用索引。但这对结果*.xlsx
的文件大小没有太大影响。
SXSSFWorkbook
以及Office Open XML
创建的所有其他apache poi
格式文件都已使用org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
压缩。该操作使用deflate作为压缩算法,并使用Deflater.DEFAULT_COMPRESSION
作为默认压缩级别。可以覆盖protected ZipArchiveOutputStream createArchiveOutputStream(OutputStream out)
的SXSSFWorkbook
来设置另一压缩级别。但这也不会对结果*.xlsx
的文件大小产生太大影响。
示例Java
代码:
import java.io.File;
import java.io.OutputStream;
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.commons.compress.archivers.zip.Zip64Mode;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import java.util.zip.Deflater;
class CreateSXSSFDifferentCompression {
static SXSSFWorkbook createSXSSFWorkbook(int compressionLevel, int rowAccessWindowSize,
boolean compressTmpFiles, boolean useSharedStringsTable) {
SXSSFWorkbook workbook = null;
if (compressionLevel != Deflater.DEFAULT_COMPRESSION) {
workbook = new SXSSFWorkbook(null, rowAccessWindowSize, compressTmpFiles, useSharedStringsTable) {
protected ZipArchiveOutputStream createArchiveOutputStream(OutputStream out) {
ZipArchiveOutputStream zos = new ZipArchiveOutputStream(out);
zos.setUseZip64(Zip64Mode.AsNeeded);
zos.setLevel(compressionLevel);
return zos;
}
};
} else {
workbook = new SXSSFWorkbook(null, rowAccessWindowSize, compressTmpFiles, useSharedStringsTable);
}
return workbook;
}
public static void main(String[] args) throws Exception {
SXSSFWorkbook workbook = null;
// uses Deflater.DEFAULT_COMPRESSION and inline strings
//workbook = createSXSSFWorkbook(Deflater.DEFAULT_COMPRESSION, 500, true, false);
// uses Deflater.DEFAULT_COMPRESSION and shared strings table
//workbook = createSXSSFWorkbook(Deflater.DEFAULT_COMPRESSION, 500, true, true);
// uses Deflater.BEST_COMPRESSION and inline strings
workbook = createSXSSFWorkbook(Deflater.BEST_COMPRESSION, 500, true, false);
// uses Deflater.BEST_COMPRESSION and shared strings table
//workbook = createSXSSFWorkbook(Deflater.BEST_COMPRESSION, 500, true, true);
int ROWS_COUNT = 2000;
int COLS_COUNT = 1000;
Sheet sheet = workbook.createSheet("Test Sheet 1");
for (int i = 1 ; i <= ROWS_COUNT; i++) {
Row row = sheet.createRow(i);
//System.out.println("Row " + i);
for(int j = 1; j <= COLS_COUNT; j++) {
row.createCell(j).setCellValue("Test " + i);
}
}
FileOutputStream out = new FileOutputStream("./Excel.xlsx");
workbook.write(out);
out.close();
workbook.close();
workbook.dispose();
File file = new File("./Excel.xlsx");
System.out.println(file.length());
}
}
这导致Excel.xlsx
文件大小为:
5,031,034字节当使用Deflater.DEFAULT_COMPRESSION和内联字符串时。
4,972,663字节当使用Deflater.DEFAULT_COMPRESSION和共享字符串表时。
4,972,915字节当使用Deflater.BEST_COMPRESSION和内联字符串时。
和4,966,749字节当使用Deflater.BEST_COMPRESSION和共享字符串表时。
使用:Java 12
,apache poi 4.1.2
,Ubuntu Linux
。
对于拥有2,000行x 1,000列的电子表格,我都不会说太大,也不会说不同设置的影响会很大。
如果Excel
本身保存*.xlsx
文件,则当内容相等时,它将创建具有大致相同文件大小的文件。因此Excel
本身使用相同的压缩级别。当然,当将*.xlsx
文件存储到*.zip
归档文件中时,可以更多地压缩它们。但是我怀疑Excel
是否能够在不先解压缩的情况下打开它们。因此,由于apache poi
创建的文件可以直接打开Excel
,因此它使用的压缩级别与Excel
相同。