POIレポートの高度な操作
前回の記事では、ExcelをExcel2003とExcel2007の2つのバージョンに分割できることを紹介しました。Excel2003はPOIでHSSFオブジェクトを使用します。シートは最大65536のデータ項目を許可します。少ないデータを処理する場合に使用できますが、Excel2003は数百万のデータを処理する場合は確かに対応できません。 ; Excel2007はPOIでXSSFオブジェクトを使用し、シートに最大1048576個のデータを格納できることを示します。これは、数百万のデータをサポートできることを示していますが、実際の操作には問題がある可能性があります。理由は、POIレポートによって生成されたオブジェクト、セルオブジェクト、フォントオブジェクトは破棄されないため、OutOfMemoryError(OOM)メモリオーバーフローのリスクが発生する可能性があります。
ミリオンデータレポートのエクスポートの概要
何百万ものデータExcelをエクスポートする場合は、基本的にExcel2007バージョンの使用についてのみ説明します。ApachePOIは、大量のデータのインポートとエクスポートを解決する3つの方法を提供します。
- ユーザーモード:ユーザーモードには多くのカプセル化されたメソッドがあり、操作は簡単ですが、作成するオブジェクトが多すぎて大量のメモリを消費します
- イベントモード:SAX(Simple API for XML)に基づくXMLの解析は、インターフェイスおよびソフトウェアパッケージです。これは、XML解析の代替手段です。DOM解析との違いは、行ごとに解析し、すべてのデータを一度にロードしないことです。メモリは、スキャン中の分析と同等です。
- SXSSFオブジェクト:主にExcelを生成するための一時的なストレージスペースを使用して、多数のExcelファイルを生成するために使用されます
何百万ものデータのエクスポート
需要分析
インターネットの時代には、何百万ものデータが生成されることが多く、データのエクスポートにはさまざまな理由があります。
解決
- 1.思考分析:
POIのXSSFWORKオブジェクトを使用してExcelレポートをエクスポートすると、すべてのデータがセルオブジェクトに一度に転送され、メモリに保存されます。すべてのセルが作成されると、Excelに書き込まれ、一度にエクスポートされます。数百万のデータエクスポートに達すると、セルオブジェクトが継続的に作成されるため、OOMまでメモリに保存されるデータはますます増えます。POIのSXSSFWORKオブジェクトは、大量のデータを処理するためにExcelから特別にエクスポートされます。
2.原理分析:
SXSSFWorkオブジェクトをインスタンス化するときに、メモリに生成されるPOIオブジェクトの数(デフォルトは100)を指定できます。メモリオブジェクトの数が指定された数に達すると、メモリ内のデータがディスクに書き込まれ、メモリ内のデータを破棄できます。 Excelのエクスポートが完了するまでループします
コード
例としてユーザーデータを取り上げます
FileUtilに追加
前のものにいくつかのマイナーな変更を加えました
package com.cn.greemes.common.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.poi.excel.BigExcelWriter;
import cn.hutool.poi.excel.ExcelUtil;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 文件操作
*/
public class FileUtil {
public static final String SYS_TEM_DIR =System.getProperty("java.io.tmpdir")+ File.separator;
public void downloadExcel(List<Map<String, String>> list, HttpServletResponse response) throws IOException {
String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx";
File file = new File(tempPath);
BigExcelWriter writer = ExcelUtil.getBigWriter(file);
// 一次性写出内容,使用默认样式,强制输出标题
writer.write(list, true);
SXSSFSheet sheet = (SXSSFSheet)writer.getSheet();
//上面需要强转SXSSFSheet 不然没有trackAllColumnsForAutoSizing方法
sheet.trackAllColumnsForAutoSizing();
//列宽自适应
// writer.autoSizeColumnAll();
//response为HttpServletResponse对象
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
//test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码
response.setHeader("Content-Disposition", "attachment;filename=file.xlsx");
ServletOutputStream out = response.getOutputStream();
// 终止后删除临时文件
file.deleteOnExit();
writer.flush(out, true);
//此处记得关闭输出Servlet流
IoUtil.close(out);
}
public void downloadExcelBySXSSF(List<Map<String, String>> list, HttpServletResponse response) throws IOException {
String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx";
File file = new File(tempPath);
//2.创建工作簿
SXSSFWorkbook workbook = new SXSSFWorkbook();
//3.构造sheet
Sheet sheet =workbook.createSheet();
//创建表头
Row row = sheet.createRow(0);
Map<String,String> mapfirst = list.get(0);
String listHead = null;
AtomicInteger headersAi = new AtomicInteger();
for (String key : mapfirst.keySet()) {
Cell cell = row.createCell(headersAi.getAndIncrement());
cell.setCellValue(key);
}
AtomicInteger datasAi = new AtomicInteger(1);
Cell cell =null;
for(Map<String, String> map : list){
Row dataRow = sheet.createRow(datasAi.getAndIncrement());
int i=0;
for (String key : map.keySet()) {
Cell cell1 = dataRow.createCell(datasAi.getAndIncrement());
String value= (String)map.get(key);
cell = dataRow.createCell(i);
cell1.setCellValue(value);
i++;
}
}
String fileName = URLEncoder.encode("用户信息.xlsx", "UTF-8");
response.setContentType("application/octet-stream");
response.setHeader("content-disposition", "attachment;filename=" + new
String(fileName.getBytes("ISO8859-1")));
response.setHeader("filename", fileName);
workbook.write(response.getOutputStream());
}
}
コントローラで設定する
@ApiOperation("导出用户数据")
@RequestMapping(value = "/export2", method = RequestMethod.GET)
@ResponseBody
public void export2(HttpServletResponse response, @RequestParam(value = "keyword", required = false) String keyword,
@RequestParam(value = "pageSize", defaultValue = "5") Integer pageSize,
@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum) throws UnsupportedEncodingException, IOException {
Page<MesAdmin> adminList = adminService.list(keyword, pageSize, pageNum);
List<Map<String,String>> list = new ArrayList();
//因为只有七条数据,所以做了多次循环添加数据
for(int i=0;i<149795;i++) {
for (MesAdmin umsAdmin : adminList.getRecords()) {
Map<String, String> map = new HashMap<>(6);
DateFormat d1 = DateFormat.getDateInstance();
map.put("姓名", umsAdmin.getUsername());
map.put("邮箱", umsAdmin.getEmail());
map.put("昵称", umsAdmin.getNickName());
map.put("备注信息", umsAdmin.getNote());
map.put("创建时间", d1.format( umsAdmin.getCreateTime()));
String loginTime ="";
if(umsAdmin.getLoginTime()!=null){
loginTime=d1.format( umsAdmin.getLoginTime());
}
map.put("最后登录时间",loginTime );
list.add(map);
}
}
fileUtil.downloadExcelBySXSSF(list,response);
}
結論:
もともとデータのエクスポートを紹介したかったのですが、何百万ものデータのエクスポートも導入する必要があることがわかりました。明日はデータのエクスポートを紹介します。
Githubアドレス:
githubアドレス:https://github.com/bangbangzhou/greemes/tree/master