java 导出之excel
Springboot之Excel导出
1.简介
excel导出用的工具是easyExcel,其中导出主要步骤是准备环境,导出模板文件流,封装表头,封装数据list,书写策略改模式。其他看官方文档。
2.环境
//具体哪个真正有用不知道,这三个绝对够了
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency> <!-- alibaba easyExcel --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.6</version> </dependency>
3.导出文件流
1 //创建输入流
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 2 EasyExcel.write(outputStream, CmXcmzVo.class).excelType(ExcelTypeEnum.XLSX) 3 // 是否自动关流 4 .autoCloseStream(Boolean.FALSE)
// 添加表单的绑定策略 5 .registerWriteHandler(new CmXczwUtils(mergeRowIndex, mergeColumnIndex,MyMergeList))
// 添加的表头 6 .head(sinkRemoveHead(bean)) 7 .sheet("显示的名字")
// 要导出的数据 8 .doWrite(list); 9 HttpHeaders httpHeaders = new HttpHeaders(); 10 String fileName = new String("xcmz.xlsx".getBytes("UTF-8"), "iso-8859-1"); 11 httpHeaders.add("content-disposition", "attachment;filename=" + fileName); 12 httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); 13 ResponseEntity<byte[]> filebyte = new ResponseEntity<byte[]>(outputStream.toByteArray(), httpHeaders, 14 HttpStatus.CREATED);
4.封装表头
//三行表头,表头的样式可调
private List<List<String>> sinkRemoveHead(CmZwkqDto bean) { String biaotou = ""; String erbiaotou=""; SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日"); List<List<String>> list = new ArrayList<List<String>>(); List<String> head0 = new ArrayList<String>(); head0.add(biaotou); head0.add(erbiaotou); head0.add("序号"); List<String> head1 = new ArrayList<String>(); head1.add(biaotou); head1.add(erbiaotou); head1.add("姓名"); list.add(head0); list.add(head1); list.add(head2); return list; }
5.封装数据
按照导出表中对应的VO对象封装list即可
VO对象
@Data @NoArgsConstructor @AllArgsConstructor @Builder @ContentRowHeight(25) //单元格内容高度 @HeadRowHeight(35) //表头 @ColumnWidth(17) //列宽,以属性为准 //头背景设置成红色 IndexedColors.RED.getIndex() @HeadStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND, fillForegroundColor = 9) public class CmXcmzVo { @ColumnWidth(8) //属性列宽 private String xh; @ColumnWidth(12) private String name; @ColumnWidth(32) private String zwmc; @ColumnWidth(12) private String csny;
}
6.书写策略
/** * * @FileName: ExcelFillCellMergeStrategy.java * * @creator lee * * @date Dec 28, 2020 * * @editor * @Description: * * @version V1.0 * */import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteTableHolder; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import java.awt.BorderLayout; import java.util.ArrayList; import java.util.List; public class CmXczwUtils implements CellWriteHandler { private int[] mergeColumnIndex; private int mergeRowIndex; private ArrayList<int[]> MyMergeList; public CmXczwUtils() { } // 构造函数当前行和当前列 public CmXczwUtils(int mergeRowIndex, int[] mergeColumnIndex, ArrayList<int[]> MyMergeList) { this.mergeRowIndex = mergeRowIndex; this.mergeColumnIndex = mergeColumnIndex; this.MyMergeList = MyMergeList; } @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { } @Override public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { } @SuppressWarnings("rawtypes") @Override public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer integer, Boolean aBoolean) { } @SuppressWarnings("rawtypes") @Override public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) { // 当前行 int curRowIndex = cell.getRowIndex(); // 当前列 int curColIndex = cell.getColumnIndex(); if (curRowIndex > mergeRowIndex) { for (int i = 0; i < mergeColumnIndex.length; i++) { if (curColIndex == mergeColumnIndex[i]) { mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex); break; } } } // 修改样式表头 if (curRowIndex == 1) { Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); CellStyle cellStyle = workbook.createCellStyle(); // 字体 cellStyle.setAlignment(HorizontalAlignment.LEFT); // 设置垂直对齐的样式为居左对齐; cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); cellStyle.setBorderRight(BorderStyle.THIN); cellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex()); cellStyle.setBorderLeft(BorderStyle.THIN); cell.setCellStyle(cellStyle); } if (curRowIndex >= 3) { Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); CellStyle cellStyle = workbook.createCellStyle(); // 字体 cellStyle.setAlignment(HorizontalAlignment.CENTER); // 设置垂直对齐的样式为居中对齐; cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); cell.setCellStyle(cellStyle); Font cellFont = workbook.createFont(); // 加粗 cellFont.setBold(false); // 设置自动换行 cellStyle.setWrapText(true); } } /** * 当前单元格向上合并 * * @param writeSheetHolder * @param cell 当前单元格 * @param curRowIndex 当前行 * @param curColIndex 当前列 */ private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) { // 获取当前行的第一列的数据和上一行的第一列数据,通过第一行数据是否相同进行合并 // 获取当前行的当前列的数据和上一行的当前列列数据,通过上一行数据是否相同进行合并 /* * Object curData = cell.getCellType() == CellType.STRING ? * cell.getStringCellValue() : cell.getNumericCellValue(); Cell preCell = * cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex); Object preData * = preCell.getCellType() == CellType.STRING ? preCell.getStringCellValue() : * preCell.getNumericCellValue(); */ // 比较当前行的第一列的单元格与上一行是否相同,相同合并当前单元格与上一行 /* * if (curData.equals(preData)) { Sheet sheet = writeSheetHolder.getSheet(); * List<CellRangeAddress> mergeRegions = sheet.getMergedRegions(); boolean * isMerged = false; for (int i = 0; i < mergeRegions.size() && !isMerged; i++) * { CellRangeAddress cellRangeAddr = mergeRegions.get(i); // * 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元 if (cellRangeAddr.isInRange(curRowIndex - * 1, curColIndex)) { sheet.removeMergedRegion(i); * cellRangeAddr.setLastRow(curRowIndex); sheet.addMergedRegion(cellRangeAddr); * isMerged = true; } } // 若上一个单元格未被合并,则新增合并单元 if (!isMerged) { CellRangeAddress * cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, * curColIndex, curColIndex); sheet.addMergedRegion(cellRangeAddress); } } */ } }
策略可以很灵活的控制导出的excel导出。主要有合并单元格,列宽,换行,单元格边框,颜色和字体。
通过判断可以定向改变内容。例如可以将阿拉伯数字改为中国的大写数字。
样式判断:用行和列做判断时是指这些行列已经渲染,例如当前列= =4,是指渲染第五列触发这个策略,从0列开始