一、背景 在後臺項目中,經常會遇到將呈現的內容導出到Excel的需求,通過都是導出單個表頭的Excel文件,如果存在級聯關係的情況下,也就需要導出多表頭的場景。今天這篇文章就是分享導出Excel單表頭或多表頭的實現,目前實現方案僅支持2行表頭場景。如有更複雜的3行表頭、4行表頭複雜需求可以自行實現。 ...
一、背景
在後臺項目中,經常會遇到將呈現的內容導出到Excel的需求,通過都是導出單個表頭的Excel文件,如果存在級聯關係的情況下,也就需要導出多表頭的場景。今天這篇文章就是分享導出Excel單表頭或多表頭的實現,目前實現方案僅支持2行表頭場景。如有更複雜的3行表頭、4行表頭複雜需求可以自行實現。
二、實現思路
1. 藉助POI包實現表頭的寫入。每個表頭其實就是一行,如果是多個表頭,無非就是將寫多行表頭,然後將需要合併的表頭進行合併,藉助POI的函數為addMergedRegion。
2. 將導出數據進行轉化為一個集合,迴圈寫入每行數據。
三、實現代碼
3.1 pom引入
<properties> <java.version>1.8</java.version> <poi.version>3.17</poi.version> <mybatis.version>1.3.2</mybatis.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>${poi.version}</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>${poi.version}</version> </dependency> </dependencies>View Code
3.2 Java代碼實現
@Getter @Setter public class ExcelHelper<T> { /** * 表格標題 */ private String title; /** * 單元格寬度 */ private int colWidth = 20; /** * 行高度 */ private int rowHeight = 20; private HSSFWorkbook workbook; /** * 表頭樣式 */ private HSSFCellStyle headStyle; /** * 主體樣式 */ private HSSFCellStyle bodyStyle; /** * 日期格式化,預設yyyy-MM-dd HH:mm:ss */ private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** * Constructor * @param title */ public ExcelHelper(String title){ this.title = title; workbook = new HSSFWorkbook(); init(); } /** * Constructor * @param title * @param colWidth * @param rowHeight */ public ExcelHelper(String title, int colWidth, int rowHeight){ this.colWidth = colWidth; this.rowHeight = rowHeight; this.title = title; workbook = new HSSFWorkbook(); init(); } /** * Constructor * @param title * @param colWidth * @param rowHeight * @param dateFormat */ public ExcelHelper(String title, int colWidth, int rowHeight, String dateFormat) { this.title = title; this.colWidth = colWidth; this.rowHeight = rowHeight; workbook = new HSSFWorkbook(); sdf = new SimpleDateFormat(dateFormat); init(); } /** * 導出Excel,適用於web導出excel * * @param sheet * @param data */ private void writeSheet(HSSFSheet sheet, List<T> data,List<Column> headerList) { try { sheet.setDefaultColumnWidth(colWidth); sheet.setDefaultRowHeightInPoints(rowHeight); createHead(headerList, sheet); writeSheetContent(headerList, data, sheet); } catch (Exception e) { throw new RuntimeException(e); } } /** * 導出表格 * @param listColumn * @param datas * @return * @throws Exception */ public InputStream exportExcel(List<Column> listColumn,List<T> datas) throws Exception { splitDataToSheets(datas,listColumn); return save(workbook); } /** * 導出表格 支持2級表頭或單表頭的Excel導出 * @param headers * @param datas * @param filePath * @throws FileNotFoundException * @throws IOException * void */ public void exportExcel(List<Column> headers,List<T> datas,String filePath) throws IOException { splitDataToSheets(datas, headers); save(workbook, filePath); } /** * 把數據寫入到單元格 * @param listColumn * @param datas * @param sheet * @throws Exception * void */ private void writeSheetContent(List<Column> listColumn,List<T> datas,HSSFSheet sheet) throws Exception { HSSFRow row; List<Column> listCol = getColumnList(listColumn); for (int i = 0, index = 2; i < datas.size(); i++, index++) { // 創建行 row = sheet.createRow(index); for (int j = 0; j < listCol.size(); j++) { Column c = listCol.get(j); createCol(row, c, datas.get(i), j); } } } /** * 創建表頭 * @param listColumn 表頭數組 * @return 返回表頭總行數 */ public void createHead(List<Column> listColumn, HSSFSheet sheetCo){ HSSFRow row = sheetCo.createRow(0); HSSFRow row2 = sheetCo.createRow(1); for(short i = 0, n = 0; i < listColumn.size(); i++){ HSSFCell cell1 = row.createCell(n); cell1.setCellStyle(headStyle); HSSFRichTextString text; List<Column> columns = listColumn.get(i).getListColumn(); //雙標題 if(CollectionUtils.isEmpty(columns)){ // 單標題 HSSFCell cell2 = row2.createCell(n); cell2.setCellStyle(headStyle); text = new HSSFRichTextString(listColumn.get(i).getContent()); sheetCo.addMergedRegion(new CellRangeAddress(0, n, 1, n)); n++; cell1.setCellValue(text); continue; } text = new HSSFRichTextString(listColumn.get(i).getContent()); cell1.setCellValue(text); // 創建第一行大標題 sheetCo.addMergedRegion(new CellRangeAddress(0, 0, n, (short) (n + columns.size() -1))); // 給標題賦值 for(int j = 0; j < columns.size(); j++){ HSSFCell cell2 = row2.createCell(n++); cell2.setCellStyle(headStyle); cell2.setCellValue(new HSSFRichTextString(columns.get(j).getContent())); } } } /** * 創建行 * @param row * @param column * @param v * @param rowIndex * @return * @throws Exception */ public int createRowVal(HSSFRow row, Column column,T v,int rowIndex) throws Exception{ // 遍歷標題 if(column.getListColumn() != null && column.getListColumn().size() > 0){ for(int i = 0; i < column.getListColumn().size(); i++){ createRowVal(row,column.getListColumn().get(i),v,rowIndex); } }else{ createCol(row,column,v,rowIndex++); } return rowIndex; } /** * 創建單元格 * @param row * @param column * @param v * @param columnIndex * @throws Exception */ public void createCol(HSSFRow row,Column column,T v,int columnIndex) throws Exception{ // 創建單元格 HSSFCell cell = row.createCell(columnIndex); // 設置單元格樣式 cell.setCellStyle(bodyStyle); Class cls = v.getClass(); Field field = cls.getDeclaredField(column.getFieldName()); // 設置些屬性是可以訪問的 field.setAccessible(true); if(field.get(v) != null){ Object value = field.get(v); if(value instanceof Date){ value = parseDate((Date)value); } HSSFRichTextString richString = new HSSFRichTextString(value.toString()); cell.setCellValue(richString); } } /** * init */ private void init(){ // 生成表頭樣式 headStyle = workbook.createCellStyle(); headStyle.setFillForegroundColor(HSSFColor.HSSFColorPredefined.AQUA.getIndex()); headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); headStyle.setBorderBottom(BorderStyle.THIN); headStyle.setBorderLeft(BorderStyle.THIN); headStyle.setBorderRight(BorderStyle.THIN); headStyle.setBorderTop(BorderStyle.THIN); headStyle.setAlignment(HorizontalAlignment.CENTER); headStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 生成一個字體 HSSFFont headFont = workbook.createFont(); headFont.setColor(HSSFColor.HSSFColorPredefined.VIOLET.getIndex()); headFont.setFontHeightInPoints((short) 12); headFont.setBold(true); // 把字體應用到當前的樣式 headStyle.setFont(headFont); // 生成並設置另一個樣式 bodyStyle = workbook.createCellStyle(); bodyStyle.setFillForegroundColor(HSSFColor.HSSFColorPredefined.WHITE.getIndex()); bodyStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); bodyStyle.setBorderBottom(BorderStyle.THIN); bodyStyle.setBorderLeft(BorderStyle.THIN); bodyStyle.setBorderRight(BorderStyle.THIN); bodyStyle.setBorderTop(BorderStyle.THIN); bodyStyle.setAlignment(HorizontalAlignment.CENTER); bodyStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 生成另一個字體 HSSFFont bodyFont = workbook.createFont(); bodyFont.setBold(false); // 把字體應用到當前的樣式 bodyStyle.setFont(bodyFont); } /** * 時間轉換 * @param date * @return * String */ private String parseDate(Date date){ String dateStr = ""; try{ dateStr = sdf.format(date); } catch (Exception e){ e.printStackTrace(); } return dateStr; } /** * 拆分sheet,因為每個sheet不能超過6526,否則會報異常 * @param data * @param listColumn * void */ private void splitDataToSheets(List<T> data,List<Column> listColumn) { int dataCount = data.size(); int maxColumn = 65535; int pieces = dataCount / maxColumn; for (int i = 1; i <= pieces; i++) { HSSFSheet sheet = workbook.createSheet(this.title + i); List<T> subList = data.subList((i - 1) * maxColumn, i * maxColumn); writeSheet(sheet, subList, listColumn); } HSSFSheet sheet = workbook.createSheet(this.title + (pieces + 1)); writeSheet(sheet, data.subList(pieces * maxColumn, dataCount), listColumn); } /** * 把column的columnList整理成一個list<column> * @param listColumn * @return * List<Column> */ private List<Column> getColumnList(List<Column> listColumn){ List<Column> listCol = new ArrayList<>(); for(int i = 0; i < listColumn.size(); i++){ List<Column> list = listColumn.get(i).getListColumn(); if(list.size() > 0){ for(Column c : list){ if(c.getFieldName() != null){ listCol.add(c); } } }else{ if(listColumn.get(i).getFieldName() != null){ listCol.add(listColumn.get(i)); } } } return listCol; } /** * 保存Excel到InputStream,此方法適合web導出excel * * @param workbook * @return */ private InputStream save(HSSFWorkbook workbook) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { workbook.write(bos); InputStream bis = new ByteArrayInputStream(bos.toByteArray()); return bis; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 保存文件 * @param workbook * @param filePath * @throws IOException */ private void save(HSSFWorkbook workbook,String filePath) throws IOException { workbook.write(new FileOutputStream(filePath)); }
四、測試類
public class ExportTest { @Data @AllArgsConstructor public class ValueObj { private String value1; private String value2; } public static void main(String[] args) throws Exception { //用於存放第一行單元格 List<Column> listColumn = new ArrayList<>(); // 用於存放第一列第二行的單元格 List<Column> list2 = new ArrayList<>(); // 創建一列,value1 表示這一列需要導出欄位的值 list2.add(new Column("標題1","value1")); list2.add(new Column("標題2","value1")); list2.add(new Column("標題3","value1")); // 用於存放第二列第二行的單元格 List<Column> list3 = new ArrayList<>(); list3.add(new Column("標題6","value2")); list3.add(new Column("標題7","value2")); //創建第一行大標題,大標題的fieldName 為 null Column c1 = new Column("標題1",null); c1.setListColumn(list2); Column c2 = new Column("標題2",null); c2.setListColumn(list3); listColumn.add(c1); listColumn.add(c2); //需要導出的數據 List<ValueObj> valueList = new ArrayList<>(); valueList.add(new ValueObj("1","11")); valueList.add(new ValueObj("2","22")); valueList.add(new ValueObj("3","33")); valueList.add(new ValueObj("4","44")); ExcelHelper<ValueObj> ta = new ExcelHelper<ValueObj>("表格",15,20); ta.exportExcel(listColumn, valueList,"D:\\outExcel.xls"); } }View Code
五、實現效果