В сегодняшнем проекте необходим экспорт в Excel. Я видел, что мои коллеги использовали SXSSFWorkbook. Раньше я не использовал этот компонент. Поскольку он используется на этот раз, давайте кратко проанализируем его. POI предоставляет HSSF, XSSF и SXSSF три способа работы с Excel. Их отличия заключаются в следующем:
HSSF:是操作Excel97-2003版本,扩展名为.xls。
XSSF:是操作Excel2007版本开始,扩展名为.xlsx。
SXSSF:是在XSSF基础上,POI3.8版本开始提供的一种支持低内存占用的操作方式,扩展名为.xlsx。
В этой статье основное внимание уделяется анализу того, как SXSSF поддерживает низкое использование памяти.
Вывод первый:SXSSF 指定了rowAccessWindowSize ,每个sheet 对应一个临时文件,当行数大于rowAccessWindowSize 时,就会向临时文件中flush, 这样就保证了内存的低占用率。当行创建完,直接从临时文件中写入到Excel中。
Одно замечание:像单元格合并类似的操作是纯内存操作,如果项目中想一次合并多行时,要注意随时观察自己机器内容的使用情况,避免出现OOM。
1 в демо
// 内存中保持100条数据, 超出的部分刷新到磁盘上
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++){
// 创建行,在这儿根据当前行数跟rowAccessWindowSize 比较,来决定从内存写入文件中。
Cell cell = row.createCell(cellnum);
String address = new CellReference(cell).formatAsString();
cell.setCellValue(address);
}
}
// rownum < 900 的数据被刷新到磁盘,不能被随机访问
for(int rownum = 0; rownum < 900; rownum++){
Assert.assertNull(sh.getRow(rownum));
}
// 最后的100条数据仍然在内存中,可以随机访问
for(int rownum = 900; rownum < 1000; rownum++){
Assert.assertNotNull(sh.getRow(rownum));
}
// 从临时文件写入Excel 文件
FileOutputStream out = new FileOutputStream("d:\\sxssf.xlsx");
wb.write(out);
out.close();
// 从磁盘上释放临时文件
wb.dispose();
2 основных пункта анализа
2.1 Создание рабочей книги SXSSF
Как показано в демо,SXSSFWorkbook wb = new SXSSFWorkbook(100);
Размер rowAccessWindowSize указан как 100, то есть
Данные строки rowAccessWindowSize кэшируются в памяти. Когда количество строк превышает rowAccessWindowSize, оно вводится из памяти во временный файл.
Временные файлы создаются в
2.2 创建Sheet
Поговорим об этом частично. Если порог превышен, временный файл сбрасывается в2.3 创建row
Часть объяснения.
2.2 Создать лист
Как показано в демо,Sheet sh = wb.createSheet();
Созданный лист, Затем в процессе создания основной функцией является создание временного файла. Один временный файл на лист. Без лишних слов давайте взглянем на реализацию createSheet.
public SXSSFSheet createSheet() {
return this.createAndRegisterSXSSFSheet(this._wb.createSheet());
}
Ядром createAndRegisterSXSSFSheet являетсяsxSheet = new SXSSFSheet(this, xSheet);
. Итак, давайте посмотрим на эту функцию:
public SXSSFSheet(SXSSFWorkbook workbook, XSSFSheet xSheet) throws IOException {
this._workbook = workbook;
this._sh = xSheet;
this._writer = workbook.createSheetDataWriter(); // 这儿创建了临时文件。
this.setRandomAccessWindowSize(this._workbook.getRandomAccessWindowSize());
this._autoSizeColumnTracker = new AutoSizeColumnTracker(this);
}
Основной логикой в createSheetDataWriter является SheetDataWriter. См. createTempFile, где создается временный файл.
public SheetDataWriter() throws IOException {
this._numberLastFlushedRow = -1;
this._fd = this.createTempFile();
this._out = this.createWriter(this._fd);
}
Что касается временных файлов:
Префикс: poi-sxssf-лист Суффикс: .xml Путь хранения: код выглядит следующим образом
private void createPOIFilesDirectory() throws IOException {
if (this.dir == null) {
String tmpDir = System.getProperty("java.io.tmpdir");
if (tmpDir == null) {
throw new IOException("Systems temporary directory not defined - set the -Djava.io.tmpdir jvm property!");
}
this.dir = new File(tmpDir, "poifiles");
}
this.createTempDirectory(this.dir);
}
2.3 Создать строку
Когда файл записывается из памяти? при создании строки. Тогда давайте посмотрим на код:
public SXSSFRow createRow(int rownum) {
int maxrow = SpreadsheetVersion.EXCEL2007.getLastRowIndex();
if (rownum >= 0 && rownum <= maxrow) {
if (rownum <= this._writer.getLastFlushedRow()) {
throw new IllegalArgumentException("Attempting to write a row[" + rownum + "] in the range [0," + this._writer.getLastFlushedRow() + "] that is already written to disk.");
} else if (this._sh.getPhysicalNumberOfRows() > 0 && rownum <= this._sh.getLastRowNum()) {
throw new IllegalArgumentException("Attempting to write a row[" + rownum + "] in the range [0," + this._sh.getLastRowNum() + "] that is already written to disk.");
} else {
SXSSFRow newRow = new SXSSFRow(this);
this._rows.put(rownum, newRow);
this.allFlushed = false;
// 这儿进行了判断,如果当前行数大于randomAccessWindowSize ,则flushRows
if (this._randomAccessWindowSize >= 0 && this._rows.size() > this._randomAccessWindowSize) {
try {
this.flushRows(this._randomAccessWindowSize);
} catch (IOException var5) {
throw new RuntimeException(var5);
}
}
return newRow;
}
} else {
throw new IllegalArgumentException("Invalid row number (" + rownum + ") outside allowable range (0.." + maxrow + ")");
}
}
Логика суждения здесьif (this._randomAccessWindowSize >= 0 && this._rows.size() > this._randomAccessWindowSize)
.
Следующие части не имеют ничего общего с низким использованием памяти, а просто анализируют несколько шагов, фактически используемых в проекте.
2.4 Запись окончательного Excel из временного файла
Запись в Excel в основном выполняется вworkbook.write(out)
. Посмотрите на код:
public void write(OutputStream stream) throws IOException {
this.flushSheets(); // 把最后不足randomAccessWindowSize 的行数 写入sheet临时文件。
File tmplFile = TempFile.createTempFile("poi-sxssf-template", ".xlsx"); // 创建了一个tmplFile临时文件,不是sheet的临时文件哈
boolean deleted;
try {
FileOutputStream os = new FileOutputStream(tmplFile);
Throwable var5 = null;
try {
// 这儿将workbook 中所有的数据都写入刚刚创建的tmplFile临时文件中。
this._wb.write(os);
}
...
ZipSecureFile zf = new ZipSecureFile(tmplFile);
var5 = null;
try {
ZipFileZipEntrySource source = new ZipFileZipEntrySource(zf);
Throwable var7 = null;
try {
// 将tmplFile 临时文件写入到目标Excel中。
this.injectData(source, stream);
}
...
} finally {
// 删除tmplFile临时文件。 注意不是sheet的临时文件哈。
deleted = tmplFile.delete();
}
if (!deleted) {
throw new IOException("Could not delete temporary file after processing: " + tmplFile);
}
}
Основная логика очень проста:
(1) Сначала запишите данные, которые меньше randomAccessSize в памяти, во временный файл листа.
(2) Запишите все данные в книге (то есть временные файлы с несколькими листами) в только что созданный временный файл tmpl.
(3) Запишите данные временного файла tmpl в целевой файл.
2.5 Удалить временные файлы
workbook.dispose();
Логика здесь.
public boolean dispose() {
boolean success = true;
Iterator var2 = this._sxFromXHash.keySet().iterator();
// 逐个遍历多个sheet
while(var2.hasNext()) {
SXSSFSheet sheet = (SXSSFSheet)var2.next();
try {
// 这儿的核心是dispose.
success = sheet.dispose() && success;
} catch (IOException var5) {
logger.log(5, new Object[]{var5});
success = false;
}
}
return success;
}
Основная логика состоит в том, чтобы пройти несколько листов, а затем выполнить удаление на каждом листе Логика удаления состоит в том, чтобы сначала сбросить, а затем удалить временный файл листа, если на листе нет выходного файла.
2.6 Об операции объединения ячеек
Как объединить ячейки:
CellRangeAddress region0 = new CellRangeAddress(rowNum, rowNum+1, column, column);
sheet.addMergedRegion(region0);
Здесь просто слияние по rowNum и столбцу.
private int addMergedRegion(CellRangeAddress region, boolean validate) {
if (region.getNumberOfCells() < 2) {
throw new IllegalArgumentException("Merged region " + region.formatAsString() + " must contain 2 or more cells");
} else {
region.validate(SpreadsheetVersion.EXCEL2007);
if (validate) {
this.validateArrayFormulas(region);
this.validateMergedRegions(region);
}
CTMergeCells ctMergeCells = this.worksheet.isSetMergeCells() ? this.worksheet.getMergeCells() : this.worksheet.addNewMergeCells();
CTMergeCell ctMergeCell = ctMergeCells.addNewMergeCell();
ctMergeCell.setRef(region.formatAsString());
return ctMergeCells.sizeOfMergeCellArray();
}
}
3 Резюме
В этой статье объединен инструмент Excel, используемый в проекте — SXSSFWorkbook, для краткого пояснения. И проанализировал часть SXSSFWorkbook, занимающую мало памяти. Надеюсь, это поможет вам~ Эта статья одновременно публикуется в Цзяньшуу-у-у. Краткое описание.com/afraid/18046332No…
4 ссылки
Отличия HSSF, XSSF и SXSSF и оптимизация экспорта в Excelблог woo woo woo.cn на.com/photographed/afraid/74…
Экспорт файла EXCEL на основе потока, анализ исходного кода SXSSFWorkbookwoo woo Краткое описание.com/afraid/no 80 ah 20 no 81…
#5 Другое
(1) При чтении чисел Excel по умолчанию отображается «.0», как это решить?Нет .OSCHINA.net/cross, чтобы узнать…