| | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import org.apache.poi.ss.usermodel.*; |
| | | import org.apache.poi.ss.util.CellRangeAddress; |
| | | import org.apache.poi.xssf.streaming.SXSSFWorkbook; |
| | | import org.springframework.core.annotation.AnnotationConfigurationException; |
| | | |
| | |
| | | import java.lang.reflect.Field; |
| | | import java.lang.reflect.Method; |
| | | import java.net.URLEncoder; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.nio.charset.Charset; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.*; |
| | | |
| | | /** |
| | | * Excel导出实现 |
| | | * @author dm |
| | | * @since 2025/03/31 16:44 |
| | | * @author Eva.Caesar Liu |
| | | * @date 2023/02/14 11:14 |
| | | */ |
| | | @Data |
| | | public class ExcelExporter<T> { |
| | |
| | | private static final String DEFAULT_SHEET_NAME = "Sheet1"; |
| | | |
| | | private Class<T> modelClass; |
| | | private static int maxrows = 50000; |
| | | |
| | | private ExcelExporter(){} |
| | | |
| | | /** |
| | | * 构造器 |
| | | * |
| | | * @param modelClass 实体Class对象 |
| | | */ |
| | | public static <T> ExcelExporter<T> build(Class<T> modelClass) { |
| | |
| | | excelExporter.setModelClass(modelClass); |
| | | return excelExporter; |
| | | } |
| | | /** |
| | | * 导出到指定输出流 |
| | | * @param data 数据 |
| | | * @param sheetName Sheet名称 |
| | | */ |
| | | public void exportWithFirstAndEnd (List<T> data, String sheetName,String first,String end, OutputStream os) { |
| | | SXSSFWorkbook sxssfWorkbook; |
| | | try { |
| | | sxssfWorkbook = new SXSSFWorkbook(); |
| | | Sheet sheet = sxssfWorkbook.createSheet(sheetName); |
| | | // 创建列头 |
| | | sheet.createFreezePane(0, 2); |
| | | sheet.addMergedRegion(new CellRangeAddress(0 ,0,0,this.getColumns().size()-1)); |
| | | Row title = sheet.createRow(0); |
| | | title.setHeight((short) 1000); |
| | | Cell c = title.createCell(0); |
| | | c.setCellValue(first); |
| | | configFirstCell(sxssfWorkbook,c); |
| | | |
| | | Row header = sheet.createRow(1); |
| | | List<ColumnInfo> columns = this.getColumns(); |
| | | for (int columnIndex = 0; columnIndex < columns.size(); columnIndex++) { |
| | | ColumnInfo column = columns.get(columnIndex); |
| | | Cell cell = header.createCell(columnIndex); |
| | | cell.setCellValue(column.columnConfig.name()); |
| | | // 列宽设置 |
| | | if (column.columnConfig.width() == -1) { |
| | | sheet.setColumnWidth(columnIndex, column.columnConfig.name().length() * 2 * 256); |
| | | } else { |
| | | sheet.setColumnWidth(columnIndex, column.columnConfig.width() * 2 * 256); |
| | | } |
| | | // 设置列头单元格 |
| | | configHeaderCell(sxssfWorkbook, cell, column.columnConfig); |
| | | } |
| | | List<CellStyle> styleList = new ArrayList<>(); |
| | | for (int columnIndex = 0; columnIndex < columns.size(); columnIndex++) { |
| | | ColumnInfo column = columns.get(columnIndex); |
| | | styleList.add( configDataCell(sxssfWorkbook, column.columnConfig)); |
| | | } |
| | | // 创建数据记录 |
| | | for (int rowIndex = 0; rowIndex < data.size(); rowIndex++) { |
| | | Row row = sheet.createRow(rowIndex + 2); |
| | | for (int columnIndex = 0; columnIndex < columns.size(); columnIndex++) { |
| | | ColumnInfo column = columns.get(columnIndex); |
| | | Cell cell = row.createCell(columnIndex); |
| | | cell.setCellValue(getCellData(column, data.get(rowIndex))); |
| | | cell.setCellStyle(styleList.get(columnIndex)); |
| | | } |
| | | } |
| | | sheet.addMergedRegion(new CellRangeAddress(data.size()+2 ,data.size()+2,0,this.getColumns().size()-1)); |
| | | Row endRow = sheet.createRow(data.size()+2); |
| | | // endRow.setHeight((short) 600); |
| | | Cell c1 = endRow.createCell(0); |
| | | c1.setCellValue(end); |
| | | c1.setCellStyle( configEndCell(sxssfWorkbook)); |
| | | sxssfWorkbook.write(os); |
| | | os.close(); |
| | | } catch (Exception e) { |
| | | throw new BusinessException(ResponseStatus.EXPORT_EXCEL_ERROR, e); |
| | | } finally { |
| | | if (os != null) { |
| | | try { |
| | | os.close(); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 导出到指定输出流 |
| | | * |
| | | * @param data 数据 |
| | | * @param sheetName Sheet名称 |
| | | * @param os 输出流 |
| | | */ |
| | | public void exportData(List<T> data, String sheetName, OutputStream os) { |
| | | public void export (List<T> data, String sheetName, OutputStream os) { |
| | | SXSSFWorkbook sxssfWorkbook; |
| | | try { |
| | | sxssfWorkbook = new SXSSFWorkbook(); |
| | | int totalSheet =1; |
| | | if( data!=null && data.size()>0){ |
| | | totalSheet = data.size()/maxrows; |
| | | if(data.size()%maxrows !=0){ |
| | | totalSheet += 1; |
| | | } |
| | | } |
| | | for (int i = 0; i < totalSheet; i++) { |
| | | List<T> list = null; |
| | | if(data.size() < maxrows * (i+1)) { |
| | | list = data.subList(maxrows*i,data.size()); |
| | | }else{ |
| | | list = data.subList(maxrows*i,maxrows*(i+1)); |
| | | } |
| | | createSheetDataBiz(list,"【"+(i+1)+"】"+sheetName,sxssfWorkbook); |
| | | } |
| | | sxssfWorkbook.write(os); |
| | | os.close(); |
| | | } catch (Exception e) { |
| | | throw new BusinessException(ResponseStatus.EXPORT_EXCEL_ERROR, e); |
| | | } finally { |
| | | if (os != null) { |
| | | try { |
| | | os.close(); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void createSheetDataBiz(List<T> data, String sheetName, SXSSFWorkbook sxssfWorkbook) throws Exception{ |
| | | Sheet sheet = sxssfWorkbook.createSheet(sheetName); |
| | | // 创建列头 |
| | | sheet.createFreezePane(0, 1); |
| | |
| | | // 设置列头单元格 |
| | | configHeaderCell(sxssfWorkbook, cell, column.columnConfig); |
| | | } |
| | | List<CellStyle> styleList = new ArrayList<>(); |
| | | for (int columnIndex = 0; columnIndex < columns.size(); columnIndex++) { |
| | | ColumnInfo column = columns.get(columnIndex); |
| | | styleList.add( configDataCell(sxssfWorkbook, column.columnConfig)); |
| | | } |
| | | // 创建数据记录 |
| | | for (int rowIndex = 0; rowIndex < data.size(); rowIndex++) { |
| | | Row row = sheet.createRow(rowIndex + 1); |
| | |
| | | ColumnInfo column = columns.get(columnIndex); |
| | | Cell cell = row.createCell(columnIndex); |
| | | cell.setCellValue(getCellData(column, data.get(rowIndex))); |
| | | // 设置数据单元格 |
| | | configDataCell(sxssfWorkbook, cell, column.columnConfig); |
| | | } |
| | | } |
| | | sxssfWorkbook.write(os); |
| | | os.close(); |
| | | } catch (Exception e) { |
| | | throw new BusinessException(ResponseStatus.EXPORT_EXCEL_ERROR, e); |
| | | } finally { |
| | | if (os != null) { |
| | | try { |
| | | os.close(); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | // 设置数据单元格样式 |
| | | cell.setCellStyle(styleList.get(columnIndex)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 导出至响应流 |
| | | * |
| | | * @param data 数据 |
| | | * @param fileName Excel文件名 |
| | | * @param sheetName Sheet名称 |
| | | * @param response HttpServletResponse对象 |
| | | */ |
| | | public void exportData(List<T> data, String fileName, String sheetName, HttpServletResponse response) { |
| | | public void export (List<T> data, String fileName, String sheetName, HttpServletResponse response) { |
| | | try { |
| | | String encodeFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()) + ".xlsx"; |
| | | String encodeFileName = URLEncoder.encode(fileName, Charset.forName("UTF-8").toString()) + ".xlsx"; |
| | | response.setHeader("Content-Disposition","attachment;filename=" + encodeFileName); |
| | | response.setContentType("application/octet-stream"); |
| | | response.setHeader("doumee-opera-type", "download"); |
| | | response.setHeader("doumee-download-filename", encodeFileName); |
| | | this.exportData(data, sheetName, response.getOutputStream()); |
| | | this.export(data, sheetName, response.getOutputStream()); |
| | | } catch (IOException e) { |
| | | throw new BusinessException(ResponseStatus.EXPORT_EXCEL_ERROR, e); |
| | | } |
| | | } |
| | | /** |
| | | * 导出至响应流 |
| | | * @param data 数据 |
| | | * @param fileName Excel文件名 |
| | | * @param sheetName Sheet名称 |
| | | * @param response HttpServletResponse对象 |
| | | */ |
| | | public void exportWithFirstAndEnd (List<T> data, String fileName, String sheetName, String first,String end ,HttpServletResponse response) { |
| | | try { |
| | | String encodeFileName = URLEncoder.encode(fileName, Charset.forName("UTF-8").toString()) + ".xlsx"; |
| | | response.setHeader("Content-Disposition","attachment;filename=" + encodeFileName); |
| | | response.setContentType("application/octet-stream"); |
| | | response.setHeader("doumee-opera-type", "download"); |
| | | response.setHeader("doumee-download-filename", encodeFileName); |
| | | this.exportWithFirstAndEnd(data, sheetName,first,end, response.getOutputStream()); |
| | | } catch (IOException e) { |
| | | throw new BusinessException(ResponseStatus.EXPORT_EXCEL_ERROR, e); |
| | | } |
| | |
| | | |
| | | /** |
| | | * 导出至响应流 |
| | | * |
| | | * @param data 数据 |
| | | * @param fileName Excel文件名 |
| | | * @param response HttpServletResponse对象 |
| | | */ |
| | | public void exportData(List<T> data, String fileName, HttpServletResponse response) { |
| | | this.exportData(data, fileName, DEFAULT_SHEET_NAME, response); |
| | | public void export (List<T> data, String fileName, HttpServletResponse response) { |
| | | |
| | | this.export(data, fileName, DEFAULT_SHEET_NAME, response); |
| | | } |
| | | /** |
| | | * 导出至响应流 |
| | | * @param data 数据 |
| | | * @param fileName Excel文件名 |
| | | * @param response HttpServletResponse对象 |
| | | */ |
| | | public void exportWithFirstAndEnd (List<T> data, String fileName,String first,String end, HttpServletResponse response) { |
| | | this.exportWithFirstAndEnd(data, fileName, DEFAULT_SHEET_NAME, first,end,response); |
| | | } |
| | | |
| | | /** |
| | | /**O |
| | | * 获取列集合 |
| | | */ |
| | | private List<ColumnInfo> getColumns () { |
| | |
| | | Field[] fields = modelClass.getDeclaredFields(); |
| | | int index = 0; |
| | | for (Field field : fields) { |
| | | ExcelExportColumn excelColumn = field.getAnnotation(ExcelExportColumn.class); |
| | | ExcelColumn excelColumn = field.getAnnotation(ExcelColumn.class); |
| | | if (excelColumn == null) { |
| | | continue; |
| | | } |
| | | if (sortedFields.get(excelColumn.index()) != null) { |
| | | throw new AnnotationConfigurationException("EVA: excel column contains the same index."); |
| | | throw new AnnotationConfigurationException("Excel column contains the same index."); |
| | | } |
| | | sortedFields.put(excelColumn.index() == -1 ? index : excelColumn.index(), new ColumnInfo(excelColumn, field)); |
| | | index++; |
| | |
| | | /** |
| | | * 配置数据单元格 |
| | | */ |
| | | private void configDataCell (SXSSFWorkbook workbook, Cell cell, ExcelExportColumn columnConfig) { |
| | | CellStyle style = workbook.createCellStyle(); |
| | | style.setAlignment(columnConfig.align()); |
| | | style.setVerticalAlignment(VerticalAlignment.CENTER); |
| | | private CellStyle configDataCell (SXSSFWorkbook workbook, ExcelColumn columnConfig) { |
| | | CellStyle configDataCellStyle = workbook.createCellStyle(); |
| | | configDataCellStyle.setAlignment(columnConfig.align()); |
| | | configDataCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); |
| | | // 设置背景 |
| | | style.setFillPattern(FillPatternType.SOLID_FOREGROUND); |
| | | style.setFillForegroundColor(columnConfig.dataBackgroundColor().getIndex()); |
| | | configDataCellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); |
| | | configDataCellStyle.setFillForegroundColor(columnConfig.dataBackgroundColor().getIndex()); |
| | | // 字体 |
| | | Font font = workbook.createFont(); |
| | | font.setFontHeightInPoints(columnConfig.fontSize()); |
| | |
| | | font.setBold(columnConfig.bold()); |
| | | // 斜体 |
| | | font.setItalic(columnConfig.italic()); |
| | | style.setFont(font); |
| | | configDataCellStyle.setFont(font); |
| | | // 边框 |
| | | configCellBorder(style); |
| | | cell.setCellStyle(style); |
| | | configCellBorder(configDataCellStyle); |
| | | configDataCellStyle.setWrapText(true); |
| | | |
| | | return configDataCellStyle; |
| | | // cell.setCellStyle(configDataCellStyle); |
| | | } |
| | | |
| | | /** |
| | | * 配置列头单元格 |
| | | */ |
| | | private void configHeaderCell (SXSSFWorkbook workbook, Cell cell, ExcelExportColumn columnConfig) { |
| | | private void configHeaderCell (SXSSFWorkbook workbook, Cell cell, ExcelColumn columnConfig) { |
| | | CellStyle style = workbook.createCellStyle(); |
| | | style.setAlignment(columnConfig.align()); |
| | | style.setVerticalAlignment(VerticalAlignment.CENTER); |
| | |
| | | // 设置边框 |
| | | configCellBorder(style); |
| | | cell.setCellStyle(style); |
| | | } |
| | | /** |
| | | * 配置列头单元格 |
| | | */ |
| | | private void configFirstCell (SXSSFWorkbook workbook, Cell cell ) { |
| | | CellStyle style = workbook.createCellStyle(); |
| | | style.setAlignment(HorizontalAlignment.CENTER); |
| | | style.setVerticalAlignment(VerticalAlignment.CENTER); |
| | | // 设置背景 |
| | | style.setFillPattern(FillPatternType.SOLID_FOREGROUND); |
| | | style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); |
| | | // 字体 |
| | | Font font = workbook.createFont(); |
| | | font.setFontHeightInPoints((short)18); |
| | | style.setFont(font); |
| | | // 设置边框 |
| | | configCellBorder(style); |
| | | cell.setCellStyle(style); |
| | | } |
| | | // public static CellStyle configEndCellStyle =null; |
| | | /** |
| | | * 配置列头单元格 |
| | | */ |
| | | private CellStyle configEndCell (SXSSFWorkbook workbook ) { |
| | | CellStyle style = workbook.createCellStyle(); |
| | | style.setAlignment(HorizontalAlignment.RIGHT); |
| | | style.setVerticalAlignment(VerticalAlignment.CENTER); |
| | | // 设置背景 |
| | | style.setFillPattern(FillPatternType.SOLID_FOREGROUND); |
| | | style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); |
| | | // 字体 |
| | | Font font = workbook.createFont(); |
| | | font.setFontHeightInPoints((short)14); |
| | | style.setFont(font); |
| | | // 设置边框 |
| | | configCellBorder(style); |
| | | return style; |
| | | |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | String stringValue = value.toString(); |
| | | // 存在自定义数据处理器 |
| | | if (!columnInfo.columnConfig.converter().equals(ExcelDataConverterAdapter.class)) { |
| | | if (!columnInfo.columnConfig.handler().equals(ExcelDataHandlerAdapter.class)) { |
| | | try { |
| | | Object instance = columnInfo.columnConfig.converter().newInstance(); |
| | | Method convertMethod = columnInfo.columnConfig.converter().getMethod("convert", Object[].class); |
| | | Object instance = columnInfo.columnConfig.handler().newInstance(); |
| | | Method formatMethod = columnInfo.columnConfig.handler().getMethod("format", Object[].class); |
| | | List<Object> args = new ArrayList<>(); |
| | | args.add(value); |
| | | for (String arg : columnInfo.columnConfig.args()) { |
| | | args.add(arg); |
| | | } |
| | | value = convertMethod.invoke(instance, new Object[]{args.toArray()}); |
| | | value = formatMethod.invoke(instance, new Object[]{args.toArray()}); |
| | | stringValue = value.toString(); |
| | | } catch (Exception e) { |
| | | throw new IllegalStateException("EVA: can not convert data by " + columnInfo.columnConfig.converter(), e); |
| | | throw new IllegalStateException("Can not format data by " + columnInfo.columnConfig.handler(), e); |
| | | } |
| | | } |
| | | // 日期处理 |
| | |
| | | return stringValue; |
| | | } |
| | | |
| | | /** |
| | | * 列信息 |
| | | */ |
| | | @Data |
| | | @AllArgsConstructor |
| | | private static class ColumnInfo { |
| | | |
| | | // 列配置 |
| | | private ExcelExportColumn columnConfig; |
| | | private ExcelColumn columnConfig; |
| | | |
| | | // 字段信息 |
| | | private Field field; |
| | | } |
| | | |