当前位置:   article > 正文

easyExcel导入导出_easyexcel导出

easyexcel导出

目录

EasyExcel工具类

pom导入依赖

1.相关代码

1.1 EasyExcel导出工具类

1.2CommonListener导入侦听器

1.3CustomMergeStrategy 合并策略(已改)

1.4 DownHandler 下拉处理(已解决大数据问题)

1.5 CustomMerge 合并单元格的注解

1.6 FieldDispose 导入自定义解析注解

1.7 DropDown 下拉注解

1.8 ExcelFieldType自定义数据解析常量

1.9 标题合并

2 .使用方法

2.1 导出导入实体--注解使用

2.2 Controller

2.3 service 调用

2.4 自定义批量方法 


EasyExcel工具类

       项目中常见的导出 一般使用 EasyExcel/POI,这里简单的列出了easyExcel 的用,easyExcel 使用简单方便,内存占用低,不会导致oom,不过导入导出要求多的话,写解析器也难受,哎本来写了很多的,结果好多找不到,然后暂时又没用到。在这里取出了常用的一些工具类,导出添加下拉框,单元格合并,多sheet导出等。导入则通过注解 解析字典值或校验唯一值,通过自定义的批量方法每500条插入一次

pom导入依赖

<!--excel导入导出-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.6</version>
</dependency>

1.相关代码

1.1 EasyExcel导出工具类

  1. /**
  2. * Excel相关处理
  3. * @author pw
  4. */
  5. public class ExcelUtil {
  6. /**
  7. * 单个sheet页导出
  8. */
  9. public static void writeExcel(HttpServletResponse response, List<?> data, String fileName, String sheetName, Class<?> clazz) throws Exception {
  10. EasyExcel.write(getOutputStream(fileName, response), clazz)
  11. .excelType(ExcelTypeEnum.XLSX).sheet(sheetName)
  12. .registerWriteHandler(getCellStyle()).doWrite(data);
  13. }
  14. /**
  15. * sheet页导出.设置下拉框
  16. */
  17. public static void writeExcel(HttpServletResponse response, List<?> data, String fileName, String sheetName,Map<Integer,List<String>> dropDownMap, Class<?> clazz) throws Exception {
  18. EasyExcel.write(getOutputStream(fileName, response), clazz)
  19. .excelType(ExcelTypeEnum.XLSX).sheet(sheetName)
  20. .registerWriteHandler(new DownHandler(dropDownMap))
  21. .registerWriteHandler(getCellStyle()).doWrite(data);
  22. }
  23. /**
  24. * 合并导出
  25. */
  26. public static void writeExcelCustomMerge(HttpServletResponse response, List<?> data, String fileName, String sheetName, Class<?> clazz) throws Exception {
  27. EasyExcel.write(getOutputStream(fileName, response), clazz).excelType(ExcelTypeEnum.XLSX).sheet(sheetName).registerWriteHandler(getCellStyle())
  28. .head(clazz)
  29. .registerWriteHandler(new CustomMergeStrategy(clazz))
  30. .doWrite(data);
  31. }
  32. /**
  33. * 自定义屏蔽字段加自定义标题
  34. */
  35. public static void downloadMergeTitle(OutputStream outputStream , String sheetName,Class<?> clazz, List<?> data, Set<String> set){
  36. EasyExcel.write(outputStream, clazz)
  37. .excludeColumnFiledNames(set)
  38. .relativeHeadRowIndex(1)
  39. .registerWriteHandler(new CustomTitleWriteHandler(clazz,sheetName,set))
  40. .registerWriteHandler(writeCenterStyleBorder())
  41. .autoCloseStream(Boolean.TRUE)
  42. .sheet(sheetName)
  43. .doWrite(data);
  44. }
  45. /**
  46. * 多个sheet页导出
  47. */
  48. public static void writeExcels(HttpServletResponse response,List<List<?>> data, String fileName, Class<?> clazz) throws Exception{
  49. ExcelWriter excelWriter = null;
  50. try{
  51. excelWriter = EasyExcel.write(getOutputStream(fileName, response), clazz).excelType(ExcelTypeEnum.XLSX).registerWriteHandler(getCellStyle()).build();
  52. for(int i=0;i<data.size();i++){
  53. WriteSheet writeSheet = EasyExcel.writerSheet(i, "sheet"+i).build();
  54. excelWriter.write(data.get(i), writeSheet);
  55. }
  56. } finally {
  57. if(excelWriter != null){
  58. excelWriter.finish();
  59. }
  60. }
  61. }
  62. /**
  63. * 模板复制导出
  64. * @param data 数据结构最外层为需要导出的sheet页个数,里层为多种数据格式的集合,一般为list和map组合成的混合list
  65. * @param templateFile 模板本地路径
  66. */
  67. public static void copyFillTemplate(HttpServletResponse response,List<List<?>> data, String fileName,String templateFile) throws Exception{
  68. //excel模板
  69. File file = new File(templateFile);
  70. ExcelWriter excelWriter = null;
  71. try (FileInputStream fileInputStream = new FileInputStream(file); ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
  72. //通过poi复制出需要的sheet个数的模板
  73. XSSFWorkbook workbook = new XSSFWorkbook(fileInputStream);
  74. workbook.setSheetName(0, "sheet1");
  75. for (int i = 1; i < data.size();i++) {
  76. workbook.cloneSheet(0, "sheet"+(i+1));
  77. }
  78. workbook.write(bos);
  79. byte[] bArray = bos.toByteArray();
  80. InputStream is = new ByteArrayInputStream(bArray);
  81. //通过easyExcel塞入参数
  82. excelWriter = EasyExcel.write(getOutputStream(fileName, response)).withTemplate(is).build();
  83. fillTemplateData(excelWriter,data);
  84. } finally {
  85. if(excelWriter != null){
  86. excelWriter.finish();
  87. }
  88. }
  89. }
  90. /**
  91. * 模板导出
  92. */
  93. public static void fillTemplate(HttpServletResponse response,List<List<?>> data, String fileName,String templateFile) throws Exception{
  94. ExcelWriter excelWriter = null;
  95. try {
  96. excelWriter = EasyExcel.write(getOutputStream(fileName, response)).withTemplate(new File(templateFile) ).build();
  97. fillTemplateData(excelWriter,data);
  98. } finally {
  99. if(excelWriter != null){
  100. excelWriter.finish();
  101. }
  102. }
  103. }
  104. /**
  105. * 填充模板数据
  106. * @param data 数据结构最外层为需要导出的sheet页个数,里层为多种数据格式的集合,一般为list和map组合成的混合list
  107. */
  108. private static void fillTemplateData(ExcelWriter excelWriter, List<List<?>> data){
  109. for (int i = 0; i < data.size(); i++) {
  110. WriteSheet writeSheet = EasyExcel.writerSheet(i).build();
  111. for(Object o:data.get(i)){
  112. excelWriter.fill(o, writeSheet);
  113. }
  114. }
  115. }
  116. /**
  117. * 设置请求
  118. */
  119. private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception {
  120. fileName = URLEncoder.encode(fileName, "UTF-8");
  121. // 告诉浏览器用什么软件可以打开此文件
  122. response.setHeader("content-Type", "application/vnd.ms-excel");
  123. response.setCharacterEncoding("utf8");
  124. response.setHeader("Content-Disposition", "attachment;filename*=utf-8'zh_cn'"+fileName+ +"."+ ExcelTypeEnum.XLSX);
  125. return response.getOutputStream();
  126. }
  127. /**
  128. * 设置样式
  129. */
  130. private static HorizontalCellStyleStrategy getCellStyle(){
  131. //表头样式
  132. WriteCellStyle headWriteCellStyle = new WriteCellStyle();
  133. //设置表头居中对齐
  134. headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
  135. //内容样式
  136. WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
  137. //设置内容靠左对齐
  138. contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
  139. return new HorizontalCellStyleStrategy(headWriteCellStyle,contentWriteCellStyle);
  140. }
  141. }

1.2CommonListener导入侦听器

插入方法已优化,使用的是1.8的consumer,传入自定义方法

  1. import com.alibaba.excel.context.AnalysisContext;
  2. import com.alibaba.excel.event.AnalysisEventListener;
  3. import com.alibaba.excel.exception.ExcelDataConvertException;
  4. import com.alibaba.excel.read.metadata.holder.ReadRowHolder;
  5. import com.cloud.common.core.excel.annotation.FieldDispose;
  6. import com.cloud.common.core.exception.CommonException;
  7. import lombok.Getter;
  8. import java.lang.reflect.Field;
  9. import java.util.*;
  10. import java.util.function.Consumer;
  11. /**
  12. * 基于EasyExcel的导入工具类,插入使用批量插入,BATCH_COUNT=500
  13. * @author pw
  14. * @param <T> 中间接收类
  15. */
  16. @Getter
  17. public class CommonListener<T> extends AnalysisEventListener<T> {
  18. /**
  19. * 执行的插入的service
  20. */
  21. private final Consumer<List<T>> p;
  22. /**
  23. * 字典
  24. */
  25. private final Map<String, Map<String,Object>> itemMap;
  26. /**
  27. * 中间类的字段
  28. */
  29. Field[] fields ;
  30. /**
  31. * 初始化参数,server 执行
  32. * @param itemMap 字典数据,从里面去对应的数据
  33. * @param p 执行方法
  34. */
  35. public CommonListener(Map<String,Map<String,Object>> itemMap,Class<T> tClass, Consumer<List<T>> p) {
  36. this.p = p;
  37. this.itemMap = itemMap;
  38. this.fields = tClass.getDeclaredFields();
  39. }
  40. /**
  41. * 批量插入数据的大小
  42. */
  43. private final int BATCH_COUNT = 500;
  44. /**
  45. * 存储数据的集合
  46. */
  47. private final List<T> rows = new ArrayList<>();
  48. /**
  49. * 存储数据的集合
  50. */
  51. private final Map<String, Set<String>> unique = new HashMap<>();
  52. /**
  53. * 存放失败的数据及原因
  54. */
  55. private final List<String> msgList = new ArrayList<>();
  56. @Override
  57. public void onException(Exception exception, AnalysisContext context) {
  58. if (exception instanceof ExcelDataConvertException) {
  59. ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
  60. msgList.add(String.format("第%s行,第%s列解析异常,数据为:%s",excelDataConvertException.getRowIndex(),
  61. excelDataConvertException.getColumnIndex(),excelDataConvertException.getCellData()));
  62. }
  63. }
  64. @Override
  65. public void invoke(T data, AnalysisContext context) {
  66. ReadRowHolder readRowHolder = context.readRowHolder();
  67. try {
  68. for ( Field field : fields ) {
  69. //得到属性值
  70. field.setAccessible(true);
  71. String value = (String) field.get(data);
  72. if(field.isAnnotationPresent(FieldDispose.class)){
  73. //获取字段注解
  74. FieldDispose annotation = field.getAnnotation(FieldDispose.class);
  75. switch (annotation.type()){
  76. //字典替换
  77. case DICT:
  78. if(!itemMap.get(annotation.value()).containsKey(value)){
  79. msgList.add(String.format("第%s行异常,原因%s,数据为:%s",readRowHolder.getRowIndex()+1,annotation.errorMessage(),value));
  80. return;
  81. }
  82. field.set(data, itemMap.get(annotation.value()).get(value));
  83. break;
  84. //唯一值判断
  85. case UNIQUE:
  86. if(unique.containsKey(annotation.value())){
  87. if(unique.get(annotation.value()).contains(value)){
  88. msgList.add(String.format("第%s行异常,原因%s,数据为:%s",readRowHolder.getRowIndex()+1,annotation.errorMessage(),value));
  89. return;
  90. }
  91. }else {
  92. Set<String> m = new HashSet<>();
  93. m.add(value);
  94. unique.put(annotation.value(),m);
  95. }
  96. break;
  97. default:break;
  98. }
  99. }
  100. }
  101. rows.add(data);
  102. if(rows.size()>=BATCH_COUNT){
  103. this.saveBatch();
  104. }
  105. }catch (Exception e){
  106. msgList.add(String.format("第%s行异常,原因:%s",readRowHolder.getRowIndex()+1,e.getMessage()));
  107. }
  108. }
  109. /**
  110. * 数据解析完成执行
  111. */
  112. @Override
  113. public void doAfterAllAnalysed(AnalysisContext context) {
  114. if(rows.size()>0){
  115. this.saveBatch();
  116. }
  117. }
  118. private void saveBatch(){
  119. try {
  120. p.accept(rows);
  121. }catch (CommonException e){
  122. rows.clear();
  123. msgList.add(e.getMessage());
  124. }
  125. }
  126. }

1.3CustomMergeStrategy 合并策略(已改)

  1. /**
  2. * 自定义单元格合并策略
  3. * @author pw
  4. */
  5. public class CustomMergeStrategy implements RowWriteHandler {
  6. /**
  7. * 主键下标
  8. */
  9. private Integer pkIndex;
  10. /**
  11. * 需要合并的列的下标集合
  12. */
  13. private final List<Integer> needMergeColumnIndex = new ArrayList<>();
  14. /**
  15. * DTO数据类型
  16. */
  17. private final Class<?> elementType;
  18. public CustomMergeStrategy(Class<?> elementType) {
  19. this.elementType = elementType;
  20. }
  21. @Override
  22. public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer integer, Integer integer1, Boolean aBoolean) {
  23. }
  24. @Override
  25. public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer integer, Boolean aBoolean) {
  26. }
  27. @Override
  28. public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {
  29. // 如果是标题,则直接返回
  30. if (isHead) {
  31. return;
  32. }
  33. // 获取当前sheet
  34. Sheet sheet = writeSheetHolder.getSheet();
  35. if (null == pkIndex) {
  36. this.lazyInit(writeSheetHolder);
  37. }
  38. // 判断是否需要和上一行进行合并
  39. // 不能和标题合并,只能数据行之间合并
  40. int rowNum = row.getRowNum();
  41. if (rowNum <= 1) {
  42. return;
  43. }
  44. // 获取上一行数据
  45. Row lastRow = sheet.getRow(row.getRowNum() - 1);
  46. // 将本行和上一行是同一类型的数据(通过主键字段进行判断),则需要合并
  47. if (lastRow.getCell(pkIndex).getStringCellValue().equalsIgnoreCase(row.getCell(pkIndex).getStringCellValue())) {
  48. boolean isMerged = false;
  49. for (Integer needMerIndex : needMergeColumnIndex) {
  50. //获得合并的区域
  51. List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
  52. for (int i = 0; i < mergeRegions.size(); i++) {
  53. CellRangeAddress cellRangeAddress = mergeRegions.get(i);
  54. // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
  55. if (cellRangeAddress.isInRange(rowNum - 1, needMerIndex)) {
  56. sheet.removeMergedRegion(i);
  57. cellRangeAddress.setLastRow(rowNum);
  58. sheet.addMergedRegionUnsafe(cellRangeAddress);
  59. isMerged = true;
  60. }
  61. }
  62. if (!isMerged) {
  63. CellRangeAddress cellRangeAddress = new CellRangeAddress(rowNum - 1, row.getRowNum(),
  64. needMerIndex, needMerIndex);
  65. sheet.addMergedRegionUnsafe(cellRangeAddress);
  66. }
  67. }
  68. }
  69. }
  70. /**
  71. * 初始化主键下标和需要合并字段的下标
  72. */
  73. private void lazyInit(WriteSheetHolder writeSheetHolder) {
  74. // 获取当前sheet
  75. Sheet sheet = writeSheetHolder.getSheet();
  76. // 获取标题行
  77. Row titleRow = sheet.getRow(0);
  78. // 获取DTO所有的属性
  79. Field[] fields = this.elementType.getDeclaredFields();
  80. // 遍历所有的字段,因为是基于DTO的字段来构建excel,所以字段数 >= excel的列数
  81. for (Field theField : fields) {
  82. // 获取@ExcelProperty注解,用于获取该字段对应在excel中的列的下标
  83. ExcelProperty easyExcelAnn = theField.getAnnotation(ExcelProperty.class);
  84. // 为空,则表示该字段不需要导入到excel,直接处理下一个字段
  85. if (null == easyExcelAnn) {
  86. continue;
  87. }
  88. // 获取自定义的注解,用于合并单元格
  89. CustomMerge customMerge = theField.getAnnotation(CustomMerge.class);
  90. // 没有@CustomMerge注解的默认不合并
  91. if (null == customMerge) {
  92. continue;
  93. }
  94. for (int index = 0; index < fields.length; index++) {
  95. Cell theCell = titleRow.getCell(index);
  96. // 当配置为不需要导出时,返回的为null,这里作一下判断,防止NPE
  97. if (null == theCell) {
  98. continue;
  99. }
  100. // 将字段和excel的表头匹配上
  101. if (easyExcelAnn.value()[0].equalsIgnoreCase(theCell.getStringCellValue())) {
  102. if (customMerge.isPk()) {
  103. pkIndex = index;
  104. }
  105. if (customMerge.needMerge()) {
  106. needMergeColumnIndex.add(index);
  107. }
  108. }
  109. }
  110. }
  111. // 没有指定主键,则异常
  112. if (null == this.pkIndex) {
  113. throw new IllegalStateException("使用@CustomMerge注解必须指定主键");
  114. }
  115. }
  116. }

1.4 DownHandler 下拉处理(已解决大数据问题)

  1. import com.alibaba.excel.write.handler.SheetWriteHandler;
  2. import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
  3. import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
  4. import com.cloud.common.core.excel.annotation.DropDown;
  5. import org.apache.poi.ss.usermodel.*;
  6. import org.apache.poi.ss.util.CellRangeAddressList;
  7. import org.apache.poi.xssf.usermodel.XSSFDataValidation;
  8. import java.lang.reflect.Field;
  9. import java.util.Map;
  10. /**
  11. * @author admin
  12. * @date 2020/5/17
  13. * @desc
  14. */
  15. public class DownHandler implements SheetWriteHandler {
  16. private final Map<Integer, String[]> dropDownMap;
  17. private int index;
  18. public DownHandler(Map<Integer, String[]> dropDownMap) {
  19. this.dropDownMap = dropDownMap;
  20. this.index = 0;
  21. }
  22. @Override
  23. public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
  24. }
  25. @Override
  26. public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
  27. Sheet sheet = writeSheetHolder.getSheet();
  28. ///开始设置下拉框 HSSFWorkbook
  29. DataValidationHelper helper = sheet.getDataValidationHelper();
  30. Field[] fields = writeWorkbookHolder.getClazz().getDeclaredFields();
  31. int length = fields.length;
  32. for (int i = 0; i < length; i++) {
  33. if(fields[i].isAnnotationPresent(DropDown.class)){
  34. dropDown(helper, sheet, i, fields[i].getDeclaredAnnotation(DropDown.class).value());
  35. }
  36. }
  37. if (dropDownMap == null) {
  38. return;
  39. }
  40. Workbook workbook = writeWorkbookHolder.getWorkbook();
  41. dropDownMap.forEach((celIndex, value) -> {
  42. if(value.length>20){
  43. dropDownBigData( helper, workbook ,sheet,celIndex, value);
  44. }else {
  45. dropDown(helper, sheet,celIndex, value);
  46. }
  47. });
  48. }
  49. private void dropDown(DataValidationHelper helper, Sheet sheet, Integer celIndex, String[] value) {
  50. if(null== value || value.length<=0){
  51. return;
  52. }
  53. this.dropDown(helper, sheet,celIndex, helper.createExplicitListConstraint(value));
  54. }
  55. private void dropDownBigData(DataValidationHelper helper,Workbook workbook,Sheet sheet, Integer celIndex, String[] v) {
  56. // 定义sheet的名称
  57. String sheetName = "sheet" + celIndex;
  58. // 1.创建一个隐藏的sheet 名称为 proviceSheet
  59. Sheet sheet1 = workbook.createSheet(sheetName);
  60. // 从第二个工作簿开始隐藏
  61. this.index++;
  62. // 设置隐藏
  63. workbook.setSheetHidden(this.index, true);
  64. // 2.循环赋值
  65. for (int i = 0, length = v.length; i < length; i++) {
  66. // i:表示你开始的行数 0表示你开始的列数
  67. sheet1.createRow(i).createCell(0).setCellValue(v[i]);
  68. }
  69. Name name = workbook.createName();
  70. name.setNameName(sheetName);
  71. //代表 以A列1行开始获取N行下拉数据
  72. name.setRefersToFormula(sheetName + "!$A$1:$A$" + (v.length));
  73. // 设置下拉
  74. this.dropDown(helper, sheet,celIndex, helper.createFormulaListConstraint(sheetName));
  75. }
  76. private void dropDown(DataValidationHelper helper, Sheet sheet, Integer celIndex, DataValidationConstraint constraint) {
  77. // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
  78. CellRangeAddressList addressList = new CellRangeAddressList(1, 100, celIndex, celIndex);
  79. // 数据有效性对象
  80. DataValidation dataValidation = helper.createValidation(constraint, addressList);
  81. // 处理Excel兼容性问题
  82. if (dataValidation instanceof XSSFDataValidation) {
  83. //数据校验
  84. dataValidation.setSuppressDropDownArrow(true);
  85. //错误提示
  86. dataValidation.setErrorStyle(DataValidation.ErrorStyle.STOP);
  87. dataValidation.createErrorBox("提示", "此值与单元格定义数据不一致");
  88. dataValidation.setShowErrorBox(true);
  89. //选定提示
  90. dataValidation.createPromptBox("填写说明:","填写内容只能为下拉中数据,其他数据将导致导入失败");
  91. dataValidation.setShowPromptBox(true);
  92. sheet.addValidationData(dataValidation);
  93. } else {
  94. dataValidation.setSuppressDropDownArrow(false);
  95. }
  96. sheet.addValidationData(dataValidation);
  97. }
  98. }

1.5 CustomMerge 合并单元格的注解

  1. /**
  2. * 自定义注解,用于判断是否需要合并以及合并的主键
  3. * @author pw
  4. */
  5. @Target({ElementType.FIELD})
  6. @Retention(RetentionPolicy.RUNTIME)
  7. @Inherited
  8. public @interface CustomMerge {
  9. /**
  10. * 是否需要合并单元格
  11. */
  12. boolean needMerge() default false;
  13. /**
  14. * 是否是主键,即该字段相同的行合并
  15. */
  16. boolean isPk() default false;
  17. }

1.6 FieldDispose 导入自定义解析注解

  1. /**
  2. * 数据来源解析
  3. * @author pw
  4. */
  5. @Retention(RetentionPolicy.RUNTIME)
  6. @Target({ ElementType.FIELD})
  7. @Documented
  8. public @interface FieldDispose {
  9. /**
  10. * 解析类型
  11. */
  12. AnalyticalType type() default AnalyticalType.DICT;
  13. /**
  14. *绑定的数据,类型dict对应字典(传入的map),其他类型对应实体类字段
  15. */
  16. String[] value();
  17. /**
  18. * 错误提示
  19. */
  20. String errorMessage() default "";
  21. /**
  22. *仅拼接下使用,拼接间隔符,数组第几位即代表第几位后添加
  23. */
  24. String[] mark() default {};
  25. }

1.7 DropDown 下拉注解

  1. /**
  2. * @author pw
  3. * @date 2022/11/17
  4. */
  5. @Documented
  6. @Retention(RetentionPolicy.RUNTIME)
  7. @Target({ElementType.FIELD})
  8. public @interface DropDown {
  9. String[] value();
  10. }

1.8 ExcelFieldType自定义数据解析常量

  1. /**
  2. * @author pw
  3. */
  4. public enum ExcelFieldType{
  5. /**
  6. * 字典
  7. */
  8. DICT ,
  9. /**
  10. * 唯一值
  11. */
  12. UNIQUE
  13. }

1.9 标题合并

  1. import com.alibaba.excel.annotation.ExcelIgnore;
  2. import com.alibaba.excel.write.handler.SheetWriteHandler;
  3. import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
  4. import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
  5. import lombok.var;
  6. import org.apache.poi.ss.usermodel.*;
  7. import org.apache.poi.ss.util.CellRangeAddress;
  8. import java.lang.reflect.Field;
  9. import java.util.Set;
  10. /**
  11. * @description: 表格标题处理
  12. * @author: pw
  13. * @date: 2023/3/2 17:00
  14. **/
  15. public class CustomTitleWriteHandler implements SheetWriteHandler {
  16. /**
  17. * 标题
  18. */
  19. private final String fileName;
  20. /**
  21. * DTO数据类型
  22. */
  23. private final Class<?> elementType;
  24. /**
  25. * 过滤的字段
  26. */
  27. private final Set<String> set;
  28. public CustomTitleWriteHandler(Class<?> elementType, String fileName, Set<String> set) {
  29. this.fileName = fileName;
  30. this.elementType = elementType;
  31. this.set = set;
  32. }
  33. @Override
  34. public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
  35. Workbook workbook = writeWorkbookHolder.getWorkbook();
  36. Sheet sheet = workbook.getSheetAt(0);
  37. Row row1 = sheet.createRow(0);
  38. row1.setHeight((short) 800);
  39. Cell cell = row1.createCell(0);
  40. //设置标题
  41. cell.setCellValue(fileName);
  42. CellStyle cellStyle = workbook.createCellStyle();
  43. cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
  44. cellStyle.setAlignment(HorizontalAlignment.CENTER);
  45. Font font = workbook.createFont();
  46. font.setBold(true);
  47. font.setFontHeight((short) 400);
  48. font.setFontName("宋体");
  49. cellStyle.setFont(font);
  50. cell.setCellStyle(cellStyle);
  51. // 获取clazz所有的属性
  52. Field[] fields = this.elementType.getDeclaredFields();
  53. //计算合并的长度
  54. int mergeLength = fields.length - 1 - (null != set ? set.size() : 0);
  55. //过滤注解屏蔽字段
  56. for(Field f:fields){
  57. if(f.isAnnotationPresent(ExcelIgnore.class)){
  58. mergeLength--;
  59. }
  60. }
  61. sheet.addMergedRegionUnsafe(new CellRangeAddress(0, 0, 0, mergeLength));
  62. }
  63. }

2 .使用方法

2.1 导出导入实体--注解使用

  1. /**
  2. * @author pw
  3. */
  4. @Data
  5. @ApiModel(value = "用户信息")
  6. @ColumnWidth(20)
  7. public class UserModel implements Serializable {
  8. @ApiModelProperty(value = "登录账号")
  9. @ExcelProperty(value = "登录账号",index=0)
  10. @FieldDispose(type = ExcelFieldType.UNIQUE,value = "username",errorMessage = "登录账号不允许重复" )
  11. private String username;
  12. @ApiModelProperty(value = "姓名")
  13. @ExcelProperty(value = "姓名",index=1)
  14. @FieldDispose(type = ExcelFieldType.UNIQUE,value ="nickName",errorMessage = "姓名不允许重复" )
  15. private String nickName;
  16. @ApiModelProperty(value = "性别")
  17. @ExcelProperty(value = "性别",index=2)
  18. @FieldDispose(value = DictCodeConstants.SEX,errorMessage = "该性别标识不存在" )
  19. private String sex;
  20. @ApiModelProperty(value = "手机号")
  21. @ExcelProperty(value = "手机号",index=3)
  22. private String phone;
  23. @ApiModelProperty(value = "密码")
  24. @ExcelProperty(value = "初始密码",index = 4)
  25. private String password;
  26. @ApiModelProperty(value = "职务")
  27. @ExcelProperty(value = "职务",index = 5)
  28. @FieldDispose(value = DictCodeConstants.USER_POST,errorMessage = "该职务标识不存在")
  29. private String post;
  30. @ApiModelProperty(value = "角色")
  31. @ExcelProperty(value ="角色",index = 6)
  32. @FieldDispose(value = "role",errorMessage = "该角色标识不存在")
  33. private String role;
  34. @ApiModelProperty("状态标识")
  35. @ExcelProperty(value = "状态标识",index = 7)
  36. @FieldDispose(value = DictCodeConstants.F_STATE,errorMessage = "该状态标识不存在")
  37. private String fstate;
  38. @ApiModelProperty("备注")
  39. @ExcelProperty(value = "备注",index = 8)
  40. private String remarks;
  41. }

2.2 Controller

  1. @ApiOperation(value = "导入数据")
  2. @PostMapping("/importData")
  3. public R<Object> importData(@RequestParam("file") MultipartFile file) throws IOException {
  4. return userService.importData(file);
  5. }
  6. @ApiOperation(value = "模板下载和导出数据")
  7. @GetMapping("/exportData")
  8. public void exportData(HttpServletResponse response, UserDTO dto, @RequestParam("type") int type) throws Exception {
  9. userService.exportData(response,dto,type);
  10. }

2.3 service 调用

  1. @Override
  2. public R<Object> importData(MultipartFile file) throws IOException {
  3. //填充对应词典
  4. Map<String,Map<String,Object>> map = new HashMap<>();
  5. map.put(DictCodeConstants.SEX,this.getDictItemKey(DictCodeConstants.SEX));
  6. map.put(DictCodeConstants.USER_POST,this.getDictItemKey(DictCodeConstants.USER_POST));
  7. map.put(DictCodeConstants.USER_CLASSIFY,this.getDictItemKey(DictCodeConstants.USER_CLASSIFY));
  8. map.put(DictCodeConstants.F_STATE,this.getDictItemKey(DictCodeConstants.F_STATE));
  9. map.put("role",sysRoleService.getRoleNameMap());
  10. //解析器
  11. CommonListener<UserModel> commonListener = new CommonListener<>(map, UserModel.class,this::excelBatch);
  12. EasyExcel.read(file.getInputStream()).head(UserModel.class).registerReadListener(commonListener).sheet().doRead();
  13. List<String> msgList = commonListener.getMsgList();
  14. if(msgList.size()>0){
  15. return R.failed(StringUtils.join(msgList,","));
  16. }
  17. return R.ok();
  18. }
  19. @Override
  20. public void exportData(HttpServletResponse response, UserDTO dto, int type) throws Exception {
  21. List<UserModel> list = new ArrayList<>();
  22. Map<Integer,String[]> dropDownMap = new HashMap<>(4);
  23. if(type == 1) {
  24. //查询需导出数据
  25. list = this.baseMapper.getExcelList(dto);
  26. }else {
  27. //填充下拉框
  28. dropDownMap.put(2,this.getDictItem(DictCodeConstants.SEX));
  29. dropDownMap.put(5,this.getDictItem(DictCodeConstants.USER_POST));
  30. dropDownMap.put(6,sysRoleService.getRoleName());
  31. dropDownMap.put(7,this.getDictItem(DictCodeConstants.USER_CLASSIFY));
  32. dropDownMap.put(8,this.getDictItem(DictCodeConstants.F_STATE));
  33. }
  34. ExcelUtil.writeExcel(response,list, String.valueOf(System.currentTimeMillis()), "科室人员", dropDownMap, UserModel.class);
  35. }
  36. /**
  37. * 获取下拉框字典
  38. * @param type 字典code
  39. * @return String[]
  40. */
  41. private String[] getDictItem(String type){
  42. List<SysDictItem> dictByType = DictUtil.getDictItemList(type);
  43. // List<SysDictItem> dictByType = dictItemService.getDictByType(type);
  44. if(null!=dictByType){
  45. return dictByType.stream().map(SysDictItem::getDictItemName).toArray(String[]::new);
  46. }
  47. return null;
  48. }
  49. /**
  50. * 获取字典 -name,key
  51. * @param type 字典code
  52. * @return List<String>
  53. */
  54. private Map<String,Object> getDictItemKey(String type){
  55. //优化后的接口 DictUtil 字典工具类,其它文章里https://blog.csdn.net/weixin_42653892/article/details/126827174
  56. return DictUtil.getDictItemNameMap(type);
  57. // List<SysDictItem> dictByType = dictItemService.getDictByType(type);
  58. // if(null!=dictByType){
  59. // return //dictByType.stream().collect(Collectors.toMap(SysDictItem::getDictItemName, SysDictItem::getDictItemCode));
  60. // }
  61. // return new HashMap<>(4);
  62. }

2.4 自定义批量方法 

  1. @Override
  2. public boolean excelBatch(Collection<UserModel> userList) {
  3. //数据处理及批量添加
  4. //返回提示
  5. String returnMsg= "";
  6. if(b.length()>0){
  7. b.append("表格存在重复数据:");
  8. returnMsg=b.toString();
  9. }
  10. if(repeatData.size()>0){
  11. returnMsg+="数据库已存在数据:";
  12. returnMsg+=StringUtils.join(repeatData,",");
  13. }
  14. if(returnMsg.length()>0){
  15. throw new CommonException(returnMsg);
  16. }
  17. return true;
  18. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/键盘狂人/article/detail/61066
推荐阅读
  

闽ICP备14008679号