SpringBoot + Vue 后台管理系统(七):POI读写
2019-06-25 本文已影响32人
Bertram_Wang
在最近项目中有使用了POI读取Excel文件数据,插入到数据库中的操作。
先了解一下POI是什么?
poi是Apache社区的一个开源项目,您可以使用Java读写Excel文件(本篇主要了解)。此外,您还可以使用Java读取和写入MS Word和MS PowerPoint文件。
具体参考文档:Apache POI;
引入依赖
<!-- POI依赖 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.0.0</version>
</dependency>
POI Excel操作(HSSF/XSSF)几种API?
1: 用户模型API: 大多数情况都可以使用。
通过创建Wordbook的实例来创建工作簿;
通过从现有的Workbook实例调用createSheet()创建表单Sheet实例;
通过从现有的Sheet实例调用createRow(rowNumber)来创建行Row实例;
通过从现有Row调用createCell(column,type)来创建单元格。
通过从Workbook实例调用write(outputStream)将其写到文件。注:需要手动关闭流。
示例:
写入
public static void main(String[] args) throws Exception {
// 创建工作蒲
Workbook workbook = new HSSFWorkbook();
// 创建表单 -- 设置名字
Sheet sheet = workbook.createSheet("POI测试写入表");
// 创建3个单元格样式
CellStyle cs = workbook.createCellStyle();
CellStyle cs2 = workbook.createCellStyle();
CellStyle cs3 = workbook.createCellStyle();
DataFormat df = workbook.createDataFormat();
// 创建2个字体对象
Font f = workbook.createFont();
Font f2 = workbook.createFont();
// 设置字体1到12点类型
f.setFontHeightInPoints((short)12);
// 把它弄成蓝色
f.setColor((short)0xc);
// 加粗
f.setBold(true);;
// 将字体2设置为10磅类型
f2.setFontHeightInPoints((short)10);
// 把它弄成红色
f2.setColor((short)Font.COLOR_RED);
// 加粗
f2.setBold(true);
f2.setStrikeout(true);
// 设置细胞stlye
cs.setFont(f);
// 设置单元格格式
cs.setDataFormat(df.getFormat( "#,## 0.0"));
// 设置一个细边框
cs2.setBorderBottom(BorderStyle.THIN);
// 填充w fg填充颜色
cs2.setFillPattern(FillPatternType.SOLID_FOREGROUND);;
// 将单元格格式设置为文本,请参阅DataFormat以获取完整列表
cs2.setDataFormat(HSSFDataFormat.getBuiltinFormat("文本"));
// 设置字体
cs2.setFont(f2);
// 创建一个包含30行(0-29)的工作表
// 一行
Row row = null;
// 一个单元格
Cell cell = null;
int rownum;
for(rownum =(short)0; rownum <30; rownum ++) {
// 创建一行
row = sheet.createRow(rownum);
// 在每一行上
if((rownum%2)== 0) {
// 使行高更大(以缇为单位的1/20)
row.setHeight((short)0x249);
}
// r.setRowNum((简称)rownum);
// 创建10个单元格(0-9)(+ = 2稍后变得明显
for(short cellnum =(short)0; cellnum <10; cellnum += 2) {
// 创建一个数字单元格
cell = row.createCell(cellnum);
// 做一些愚蠢的数学来证明小数
cell.setCellValue(rownum * 10000 + cellnum +(((double)rownum / 1000) +((double)cellnum / 10000)));
// 创建一个字符串单元格(请参阅为什么+ = 2)
cell = row.createCell((short)(cellnum + 1));
// 在每一行上
if((rownum%2)== 0) {
//将此单元格设置为我们定义的第一个单元格样式
cell.setCellStyle(cs);
//将单元格的字符串值设置为“Test”
cell.setCellValue("Test");
}
else {
cell.setCellStyle(cs2);
//将单元格的字符串值设置为“\ u0422 \ u0435 \ u0441 \ u0442”
cell.setCellValue(" u0422 u0435 u0441 u0442");
}
//使这个专栏更宽一些
sheet.setColumnWidth((short)(cellnum + 1), (short)((50 * 8)/((double)1/20)));
}
}
//使用BLANKS在底部的行上绘制一个粗黑色边框
//前进2行
rownum ++;
rownum ++;
row = sheet.createRow(rownum);
//将第三个样式定义为默认样式
//除了底部有一个粗黑边框
cs3.setBorderBottom(BorderStyle.THICK);
//创建50个单元格
for(short cellnum =(short)0; cellnum <50; cellnum ++) {
//创建一个空白类型单元格(没有值)
cell = row.createCell(cellnum);
//将其设置为粗黑边框样式
cell.setCellStyle(cs3);
}
//结束绘制厚黑色边框
// 测试文件
String filePath = "D://POI-test/workbook.xls";
// 创建一个新文件
FileOutputStream out = new FileOutputStream(filePath);
// 将工作簿写入输出流
// 关闭我们的文件
workbook.write(out);
out.close();
// 关闭工作蒲
workbook.close();
}
读取
public static void read() throws Exception {
// 测试文件
String filePath = "D://POI-test/workbook.xls";
// 输入流
FileInputStream inp = new FileInputStream(filePath);
Workbook workbook = new HSSFWorkbook(inp);
Sheet sheet = workbook.getSheetAt(0);
// 迭代所有的行
Iterator<Row> rowIterator = sheet.rowIterator();
// 数据
while (rowIterator.hasNext()) {
Row row = rowIterator.next();
Iterator<Cell> cellIterator = row.cellIterator();
System.out.print("第" + row.getRowNum() + "行: =》 { ");
while (cellIterator.hasNext()) {
Cell cell = cellIterator.next();
CellType cellType = cell.getCellType();
Object cellValue;
switch (cellType) {
case _NONE:
cellValue = cell.getStringCellValue();
break;
case NUMERIC:
cellValue = cell.getNumericCellValue();
break;
case STRING:
cellValue = cell.getStringCellValue();
break;
case FORMULA:
cellValue = cell.getDateCellValue();
break;
case BLANK:
cellValue = "";
break;
case BOOLEAN:
cellValue = cell.getBooleanCellValue();
break;
case ERROR:
cellValue = cell.getErrorCellValue();
break;
default:
cellValue = null;
break;
}
int columnIndex = cell.getColumnIndex();
int rowIndex = cell.getRowIndex();
System.out.print("(行:列 -" + rowIndex + ":" + columnIndex);
System.out.print("==" + cellValue + ")");
}
System.out.println();
}
inp.close();
workbook.close();
}
2:事件驱动API: 仅限于读取(HSSF)
相对于用户模型读取,事件驱动API的优势在于占用内存较小。
示例:
/**
* 这个例子展示了如何使用事件API来读取文件。
*/
public class EventExample implements HSSFListener {
private SSTRecord sstrec;
/**
* 此方法侦听传入的记录并根据需要处理它们。
* @param record 阅读时发现的记录
*/
public void processRecord(Record record) {
// record.getSid() 返回此记录的非静态id版本
switch (record.getSid()) {
// BOFRecord 既可以表示工作表的开头,也可以表示工作簿的开头
case BOFRecord.sid: // 文件的開始
BOFRecord bof = (BOFRecord) record;
if (bof.getType() == bof.TYPE_WORKBOOK) {
System.out.println("Encountered workbook");
// 分配给类级成员
} else if (bof.getType() == bof.TYPE_WORKSHEET) {
System.out.println("Encountered sheet reference");
}
break;
case BoundSheetRecord.sid: // 工作蒲纪录
BoundSheetRecord bsr = (BoundSheetRecord) record;
System.out.println("工作蒲名稱: " + bsr.getSheetname());
break;
case RowRecord.sid: // 行记录
RowRecord rowrec = (RowRecord) record;
System.out.println("找到一行, 第一列: " + rowrec.getFirstCol() + " 最后一列: " + rowrec.getLastCol());
break;
case NumberRecord.sid: // 包含一个数值单元格值
NumberRecord numrec = (NumberRecord) record;
System.out.println("单元格: " + numrec.getValue() + " 在 "+ numrec.getRow() + "行 " + numrec.getColumn() + " 列 ");
break;
// SSTRecords存储Excel中使用的惟一字符串数组
case SSTRecord.sid: // 静态字符串
sstrec = (SSTRecord) record;
for (int k = 0; k < sstrec.getNumUniqueStrings(); k++) {
System.out.println("字符串表值 " + k + " = " + sstrec.getString(k));
}
break;
case LabelSSTRecord.sid: // 引用共享字符串表中的字符串
LabelSSTRecord lrec = (LabelSSTRecord) record;
System.out.println("找到带值的字符串单元格:" + sstrec.getString(lrec.getSSTIndex()));
break;
}
}
/**
* Read an excel file and spit out what we find.
*
* @param args Expect one argument that is the file to read.
* @throws IOException When there is an error processing the file.
*/
public static void main(String[] args) throws IOException {
// 测试文件
String filePath = "D://POI-test/workbook.xls";
// 使用指定的输入文件创建一个新的文件输入流
FileInputStream fin = new FileInputStream(filePath);
// 创建一个新的org.apache.poi.poifs.filesystem.Filesystem
POIFSFileSystem poifs = new POIFSFileSystem(fin);
// 在InputStream中获取工作簿(excel部分)流
InputStream din = poifs.createDocumentInputStream("Workbook");
// 构造HSSFRequest 对象
HSSFRequest req = new HSSFRequest();
// 使用上面所示的侦听器惰性侦听所有记录
req.addListenerForAllRecords(new EventExample());
// 创建事件工厂
HSSFEventFactory factory = new HSSFEventFactory();
// 根据文档输入流处理事件
factory.processEvents(req, din);
// 处理完所有事件后,关闭文件输入流
fin.close();
din.close();
poifs.close();
// 完成
System.out.println("done.");
}
}
3:事件API扩展(仅限HSSF)
监听器将被调用额外的虚拟记录。这些虚拟记录应提醒你文件中不存在的记录(例如尚未编辑的单元格),并允许您处理这些记录。
- org.apache.poi.hssf.eventusermodel.dummyrecord.MissingRowDummyRecord
在行记录阶段(通常在单元格记录之前发生)期间调用的。 - org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord
在单元格记录阶段调用的。 - org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord
在给定行的最后一个单元格之后调用的。
示例:
/**
* 一个XLS -> CSV处理器,它使用了MissingRecordAware
* EventModel代码,以确保它输出所有列和行。
*/
public class XLS2CSVmra implements HSSFListener {
private int minColumns;
private POIFSFileSystem fs;
private PrintStream output;
private int lastRowNumber;
private int lastColumnNumber;
/** 应该输出公式,还是它的值? */
private boolean outputFormulaValues = true;
/** 解析公式 */
private SheetRecordCollectingListener workbookBuildingListener;
private HSSFWorkbook stubWorkbook;
// 在处理过程中收集的记录
private SSTRecord sstRecord;
private FormatTrackingHSSFListener formatListener;
/** sheet信息 */
private int sheetIndex = -1;
private BoundSheetRecord[] orderedBSRs;
private List<BoundSheetRecord> boundSheetRecords = new ArrayList<>();
// 用于处理带字符串结果的公式
private int nextRow;
private int nextColumn;
private boolean outputNextStringRecord;
/**
* 创建一个新的XLS -> CSV转换器
* @param fs 要处理的POIFSFileSystem
* @param output 输出CSV到的PrintStream
* @param minColumns 要输出的列数的最小值,或-1表示没有最小值
*/
public XLS2CSVmra(POIFSFileSystem fs, PrintStream output, int minColumns) {
this.fs = fs;
this.output = output;
this.minColumns = minColumns;
}
/**
* 创建一个新的XLS -> CSV转换器
* @param filename 要处理的文件
* @param minColumns 要输出的列数的最小值,或-1表示没有最小值
*/
public XLS2CSVmra(String filename, int minColumns) throws IOException, FileNotFoundException {
this(
new POIFSFileSystem(new FileInputStream(filename)),
System.out, minColumns
);
}
/**
* 启动对XLS文件到CSV的处理
*/
public void process() throws IOException {
MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this);
formatListener = new FormatTrackingHSSFListener(listener);
HSSFEventFactory factory = new HSSFEventFactory();
HSSFRequest request = new HSSFRequest();
if(outputFormulaValues) {
request.addListenerForAllRecords(formatListener);
} else {
workbookBuildingListener = new SheetRecordCollectingListener(formatListener);
request.addListenerForAllRecords(workbookBuildingListener);
}
factory.processWorkbookEvents(request, fs);
}
/**
* 主HSSFListener方法,处理事件,并在处理文件时输出CSV。
*/
@Override
public void processRecord(Record record) {
int thisRow = -1;
int thisColumn = -1;
String thisStr = null;
switch (record.getSid())
{
case BoundSheetRecord.sid:
boundSheetRecords.add((BoundSheetRecord)record);
break;
case BOFRecord.sid:
BOFRecord br = (BOFRecord)record;
if(br.getType() == BOFRecord.TYPE_WORKSHEET) {
// 创建子工作簿
if(workbookBuildingListener != null && stubWorkbook == null) {
stubWorkbook = workbookBuildingListener.getStubHSSFWorkbook();
}
// 输出工作表名称
// 其工作原理是,根据BSRs的BOFRecords的位置对其进行排序,然后知道我们按照字节偏移顺序处理BOFRecords
sheetIndex++;
if(orderedBSRs == null) {
orderedBSRs = BoundSheetRecord.orderByBofPosition(boundSheetRecords);
}
output.println();
output.println(
orderedBSRs[sheetIndex].getSheetname() +
" [" + (sheetIndex+1) + "]:"
);
}
break;
case SSTRecord.sid:
sstRecord = (SSTRecord) record;
break;
case BlankRecord.sid:
BlankRecord brec = (BlankRecord) record;
thisRow = brec.getRow();
thisColumn = brec.getColumn();
thisStr = "";
break;
case BoolErrRecord.sid:
BoolErrRecord berec = (BoolErrRecord) record;
thisRow = berec.getRow();
thisColumn = berec.getColumn();
thisStr = "";
break;
case FormulaRecord.sid:
FormulaRecord frec = (FormulaRecord) record;
thisRow = frec.getRow();
thisColumn = frec.getColumn();
if(outputFormulaValues) {
if(Double.isNaN( frec.getValue() )) {
outputNextStringRecord = true;
nextRow = frec.getRow();
nextColumn = frec.getColumn();
} else {
thisStr = formatListener.formatNumberDateCell(frec);
}
} else {
thisStr = '"' +
HSSFFormulaParser.toFormulaString(stubWorkbook, frec.getParsedExpression()) + '"';
}
break;
case StringRecord.sid:
if(outputNextStringRecord) {
StringRecord srec = (StringRecord)record;
thisStr = srec.getString();
thisRow = nextRow;
thisColumn = nextColumn;
outputNextStringRecord = false;
}
break;
case LabelRecord.sid:
LabelRecord lrec = (LabelRecord) record;
thisRow = lrec.getRow();
thisColumn = lrec.getColumn();
thisStr = '"' + lrec.getValue() + '"';
break;
case LabelSSTRecord.sid:
LabelSSTRecord lsrec = (LabelSSTRecord) record;
thisRow = lsrec.getRow();
thisColumn = lsrec.getColumn();
if(sstRecord == null) {
thisStr = '"' + "(No SST Record, can't identify string)" + '"';
} else {
thisStr = '"' + sstRecord.getString(lsrec.getSSTIndex()).toString() + '"';
}
break;
case NoteRecord.sid:
NoteRecord nrec = (NoteRecord) record;
thisRow = nrec.getRow();
thisColumn = nrec.getColumn();
// TODO: Find object to match nrec.getShapeId()
thisStr = '"' + "(TODO)" + '"';
break;
case NumberRecord.sid:
NumberRecord numrec = (NumberRecord) record;
thisRow = numrec.getRow();
thisColumn = numrec.getColumn();
// Format
thisStr = formatListener.formatNumberDateCell(numrec);
break;
case RKRecord.sid:
RKRecord rkrec = (RKRecord) record;
thisRow = rkrec.getRow();
thisColumn = rkrec.getColumn();
thisStr = '"' + "(TODO)" + '"';
break;
default:
break;
}
// 处理新行
if(thisRow != -1 && thisRow != lastRowNumber) {
lastColumnNumber = -1;
}
// 处理缺失列
if(record instanceof MissingCellDummyRecord) {
MissingCellDummyRecord mc = (MissingCellDummyRecord)record;
thisRow = mc.getRow();
thisColumn = mc.getColumn();
thisStr = "";
}
// 如果我们有东西要打印出来,就打印出来
if(thisStr != null) {
if(thisColumn > 0) {
output.print(',');
}
output.print(thisStr);
}
// 更新列和行计数
if(thisRow > -1)
lastRowNumber = thisRow;
if(thisColumn > -1)
lastColumnNumber = thisColumn;
// 行结束端
if(record instanceof LastCellOfRowDummyRecord) {
// 如果需要,打印出任何缺少的逗号
if(minColumns > 0) {
// 列是基于0的
if(lastColumnNumber == -1) { lastColumnNumber = 0; }
for(int i=lastColumnNumber; i<(minColumns); i++) {
output.print(',');
}
}
// 我们到了新一行
lastColumnNumber = -1;
// 结束行
output.println();
}
}
public static void main(String[] args) throws Exception {
String filePath = "D://POI-test/workbook.xls";
XLS2CSVmra xls2csv = new XLS2CSVmra(filePath, 10);
xls2csv.process();
}
}
4:XSSF和SAX(事件API)
使用内存较小读取大文件是选择方案,
示例
/**
* @Author Bertram.wang
* @Date 2019年6月25日 下午5:03:11
* @sign POIExcelUtils.java
*/
public class POIExcelUtils {
private XSSFReader xssfReader;
//获取一行时最小数组长度
private final int minColumnCount;
private int currentRow = 0;
private int sheetIndex = -1;
private String[] record;
private int thisColumnIndex = -1;
// 日期标志
private boolean dateFlag;
// 数字标志
private boolean numberFlag;
private boolean isTElement;
private RowReader rowReader;
public void setRowReader(RowReader rowReader) {
this.rowReader = rowReader;
}
/**
* 构造方法
*/
public POIExcelUtils (String filename,int minCols) throws Exception {
if (StringUtils.isEmpty(filename)) throw new Exception("文件名不能空");
this.minColumnCount = minCols;
record = new String[this.minColumnCount];
OPCPackage pkg = OPCPackage.open(filename);
init(pkg);
}
public POIExcelUtils (InputStream is,int minCols) throws Exception {
if (null == is) throw new Exception("文件不能空");
this.minColumnCount = minCols;
record = new String[this.minColumnCount];
OPCPackage pkg = OPCPackage.open(is);
init(pkg);
}
private void init (OPCPackage pkg) throws IOException, OpenXML4JException {
xssfReader = new XSSFReader(pkg);
}
/**
* 获取sheet
* @throws Exception
*/
public void process() throws Exception {
SharedStringsTable sst = xssfReader.getSharedStringsTable();
XMLReader parser = fetchSheetParser(sst);
Iterator<InputStream> it = xssfReader.getSheetsData();
while (it.hasNext()) {
sheetIndex++;
InputStream sheet = it.next();
InputSource sheetSource = new InputSource(sheet);
parser.parse(sheetSource);
sheet.close();
}
}
/**
* 加载sax 解析器
* @param sst
* @return
* @throws SAXException
*/
private XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException {
XMLReader parser = XMLReaderFactory.createXMLReader("com.sun.org.apache.xerces.internal.parsers.SAXParser");
ContentHandler handler = new PagingHandler(sst);
parser.setContentHandler(handler);
return parser;
}
private int nameToColumn(String name) {
int column = -1;
for (int i = 0; i < name.length(); ++i) {
int c = name.charAt(i);
column = (column + 1) * 26 + c - 'A';
}
return column;
}
/**
* See org.xml.sax.helpers.DefaultHandler javadocs
*/
private class PagingHandler extends DefaultHandler {
private SharedStringsTable sst;
private String lastContents;
private boolean nextIsString;
private String index = null;
private PagingHandler(SharedStringsTable sst) {
this.sst = sst;
}
/**
* 开始元素
*/
@Override
public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
if (name.equals("c")) {
index = attributes.getValue("r");
int firstDigit = -1;
for (int c = 0; c < index.length(); ++c) {
if (Character.isDigit(index.charAt(c))) {
firstDigit = c;
break;
}
}
thisColumnIndex = nameToColumn(index.substring(0, firstDigit));
// 判断是否是新的一行
if (Pattern.compile("^A[0-9]+$").matcher(index).find()) {
currentRow++;
}
String cellType = attributes.getValue("t");
if (cellType != null && cellType.equals("s")) {
nextIsString = true;
} else {
nextIsString = false;
}
// 日期格式
String cellDateType = attributes.getValue("s");
if ("1".equals(cellDateType)) {
dateFlag = true;
} else {
dateFlag = false;
}
String cellNumberType = attributes.getValue("s");
if ("2".equals(cellNumberType)) {
numberFlag = true;
} else {
numberFlag = false;
}
}
// 当元素为t时
if ("t".equals(name)) {
isTElement = true;
} else {
isTElement = false;
}
lastContents = "";
}
/**
* 获取value
*/
@Override
public void endElement(String uri, String localName, String name) throws SAXException {
if (nextIsString) {
int idx = Integer.parseInt(lastContents);
lastContents = sst.getItemAt(idx).getString();
nextIsString = false;
}
// t元素也包含字符串
if (isTElement) {
String value = lastContents.trim();
record[thisColumnIndex] = value;
isTElement = false;
// v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引
// 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符
} else if ("v".equals(name)) {
String value = lastContents.trim();
value = value.equals("") ? " " : value;
// 日期格式处理
if (dateFlag) {
try {
Date date = HSSFDateUtil.getJavaDate(Double.valueOf(value));
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
value = dateFormat.format(date);
} catch (NumberFormatException e) {
}
}
// 数字类型处理
if (numberFlag) {
try {
BigDecimal bd = new BigDecimal(value);
value = bd.setScale(3, BigDecimal.ROUND_UP).toString();
} catch (Exception e) {
}
}
record[thisColumnIndex] = value;
} else {
if (name.equals("row")) {
if (minColumnCount > 0) {
rowReader.getRows(sheetIndex, currentRow, record.clone());
for (int i = 0; i < record.length; i++) {
record[i] = null;
}
}
}
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
lastContents += new String(ch, start, length);
}
}
}
行处理
/**
* @Author Bertram.wang
* @Date 2019年6月25日 下午5:14:40
* @sign RowReader.java Excel行处理
*/
public class RowReader{
private static final Logger log = LoggerFactory.getLogger(RowReader.class);
private Integer curRow=-1;
private List<Object> contents = new ArrayList<>();
private Map<String, Object> map = new LinkedHashMap<>();
private ObjectMapper objMap = new ObjectMapper();
private Class<?> clazz;
private RowReader() {
super();
}
public static RowReader create(Class<?> clazz) {
RowReader rowReader = new RowReader();
rowReader.clazz = clazz;
return rowReader;
}
public void getRows(int sheetIndex, int curRow, String[] rows) {
if(this.curRow!=curRow) {
if (curRow == 1) {
for (String cell : rows) {
if (cell != null) {
map.put(cell, "");
}
}
}else {
Set<String> keySet = map.keySet();
Object[] array = keySet.toArray();
for (int i = 0; i < array.length; i++) {
map.put(array[i].toString(), rows[i]);
}
Object readValue;
try {
readValue = objMap.readValue(objMap.writeValueAsString(map), clazz);
} catch (Exception e) {
log.error(e.getMessage());
readValue = null;
}
contents.add(readValue);
}
this.curRow=curRow;
}
}
public List<Object> getContents(){
return this.contents;
}
}
主方法测试:
@org.junit.Test
public void TestOne() throws Exception {
RowReader rowReader = RowReader.create(User.class);
POIExcelUtils poiExcelUtils = new POIExcelUtils("D://POI-test/test.xlsx", 3);
poiExcelUtils.setRowReader(rowReader);
poiExcelUtils.process();
List<User> contents = (List<User>)rowReader.getContents();
System.out.println(contents);
}
执行日志:[User(roleIds=null, name=6, password=7), User(roleIds=null, name=8, password=9)]
测试文件
5:SXSSF(Streaming Usermodel API)
SXSSF是XSSF的API兼容流式扩展,用于在必须生成非常大的电子表格且堆空间有限。
SXSSF通过限制缓存的行数来实现低内存占用写操作。
示例:
/**
* <p></p>
* @param datas 需要写入得数据
* @param filePath 文件链接
* @param minCellnum 列数
* @param maxRowNum 最大行数
* @return
* @throws IOException
*/
public static Boolean write(List<?> datas, String filePath, Integer minCellnum, Integer maxRowNum) throws Exception {
// 创建文件
SXSSFWorkbook wb = new SXSSFWorkbook(-1);
// 创建工作表
Sheet sh = wb.createSheet();
// 数据量
int size = datas.size();
// 获取需要输出的字段集合
Class<? extends Object> clazz = datas.get(0).getClass();
// 获取实体属性集合
Field[] fields = clazz.getDeclaredFields();
List<Field> writeFileId = new ArrayList<>();
// 排除被 final 或 transient 修饰的字段
for (Field field : fields) {
String modifier = Modifier.toString(field.getModifiers());
if (!(modifier.indexOf("final") > -1 || modifier.indexOf("transient") > -1)) {
writeFileId.add(field);
}
}
// ========================开始设置第一行
Row row0 = sh.createRow(0);
for(int cellnum = 0; cellnum < writeFileId.size(); cellnum++){
Field field = writeFileId.get(cellnum);
Cell cell = row0.createCell(cellnum);
String cellValue = field.getName();
cell.setCellValue(cellValue);
}
// ==========================结束设置第一行
// 循环数据创建行
for(int rownum = 0; rownum < size; rownum++){
// 第一行被占据了
Row row = sh.createRow(rownum + 1);
for(int cellnum = 0; cellnum < writeFileId.size(); cellnum++){
Field field = writeFileId.get(cellnum);
// 设置属性可以直接的进行访问
field.setAccessible(true);
Object obj; // 数据实体对象
String cellValue; // 单元格的值
Cell cell = row.createCell(cellnum);
// 属性是基础数据类型
if (field.getType().isPrimitive()) {
cellValue = field.get(datas.get(rownum)).toString();
}else {
obj = field.getType().newInstance();
// 属性是日期类型
if (obj instanceof Date) {
Date date = (Date)field.get(datas.get(rownum));
cellValue = DateUtils.format(date, DateUtils.FORMAT_SEC_EN);
} else {
cellValue = field.get(datas.get(rownum)).toString();
}
}
cell.setCellValue(cellValue);
}
if(rownum % maxRowNum == 0) {
((SXSSFSheet)sh).flushRows(maxRowNum);
}
}
FileOutputStream out = new FileOutputStream(filePath);
wb.write(out);
out.close();
wb.dispose();
wb.close();
return true;
}
测试: 从测试文件test.xlsx读取出来的数据写到文件test2.xlsx
@org.junit.Test
public void TestOne() throws Exception {
RowReader rowReader = RowReader.create(User.class);
POIExcelUtils poiExcelUtils = new POIExcelUtils("D://POI-test/test.xlsx", 3);
poiExcelUtils.setRowReader(rowReader);
poiExcelUtils.process();
List<User> contents = (List<User>)rowReader.getContents();
Boolean write = POIWriteUtils.write(contents, "D://POI-test/test2.xlsx", 2, 100);
if (write) {
log.info(write.toString());
}
}