From 45d3718ed61c018207b34847fb0a74a6b969a8fc Mon Sep 17 00:00:00 2001 From: lishangbu Date: Fri, 14 Jun 2019 13:10:30 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20Introducing=20new=20features.?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0Excel=E5=AF=BC=E5=85=A5=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E5=8A=9F=E8=83=BD,=E5=9F=BA=E4=BA=8Ealibaba=E7=9A=84easyexcel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pigx-common/pigx-common-excel/pom.xml | 28 ++ .../excel/annotation/CellFontFormat.java | 24 ++ .../excel/annotation/CellStyleFormat.java | 69 +++++ .../excel/exception/ExcelException.java | 21 ++ .../excel/factory/ExcelWriterFactory.java | 63 +++++ .../excel/handler/PreWriterHandler.java | 116 ++++++++ .../common/excel/listener/ExcelListener.java | 77 ++++++ .../common/excel/util/BeanCopyUtils.java | 171 ++++++++++++ .../common/excel/util/ExcelUtils.java | 251 ++++++++++++++++++ pigx-common/pom.xml | 5 +- pom.xml | 1 + 11 files changed, 824 insertions(+), 2 deletions(-) create mode 100644 pigx-common/pigx-common-excel/pom.xml create mode 100644 pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/annotation/CellFontFormat.java create mode 100644 pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/annotation/CellStyleFormat.java create mode 100644 pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/exception/ExcelException.java create mode 100644 pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/factory/ExcelWriterFactory.java create mode 100644 pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/handler/PreWriterHandler.java create mode 100644 pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/listener/ExcelListener.java create mode 100644 pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/util/BeanCopyUtils.java create mode 100644 pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/util/ExcelUtils.java diff --git a/pigx-common/pigx-common-excel/pom.xml b/pigx-common/pigx-common-excel/pom.xml new file mode 100644 index 000000000..5264bb958 --- /dev/null +++ b/pigx-common/pigx-common-excel/pom.xml @@ -0,0 +1,28 @@ + + + + pigx-common + com.pig4cloud + 3.2 + + 4.0.0 + + pigx-common-excel + pigx Excel导入导出工具,基于easyexcel + + + com.alibaba + easyexcel + ${easyexcel.version} + + + + com.pig4cloud + pigx-common-core + true + + + + \ No newline at end of file diff --git a/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/annotation/CellFontFormat.java b/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/annotation/CellFontFormat.java new file mode 100644 index 000000000..ee2068e97 --- /dev/null +++ b/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/annotation/CellFontFormat.java @@ -0,0 +1,24 @@ +package com.pig4cloud.common.excel.annotation; + +import org.apache.poi.ss.usermodel.IndexedColors; + +import java.lang.annotation.*; + +/** + * 单元格字体 + * + * @author lishangbu + * @date 2019-06-11 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface CellFontFormat { + + String fontName() default "宋体"; + + short fontHeightInPoints() default 11; + + IndexedColors fontColor() default IndexedColors.BLACK; + +} \ No newline at end of file diff --git a/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/annotation/CellStyleFormat.java b/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/annotation/CellStyleFormat.java new file mode 100644 index 000000000..2baddcb44 --- /dev/null +++ b/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/annotation/CellStyleFormat.java @@ -0,0 +1,69 @@ +package com.pig4cloud.common.excel.annotation; + +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.VerticalAlignment; + +import java.lang.annotation.*; + +/** + * 单元格样式 + * + * @author lishangbu + * @date 2019-06-11 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface CellStyleFormat { + + /** + * 水平居中方式 默认居中 + * @see HorizontalAlignment + */ + HorizontalAlignment horizontalalignment() default HorizontalAlignment.CENTER; + + /** + * + * 垂直居中方式 默认居中 + * @see VerticalAlignment + */ + VerticalAlignment verticalAlignment() default VerticalAlignment.CENTER; + + + /** + * 边框方式 默认无 + * @see BorderStyle + */ + BorderStyle borderStyle() default BorderStyle.NONE; + + + /** + * 边框颜色 默认白 + * @see IndexedColors + */ + IndexedColors borderColor() default IndexedColors.WHITE; + + + /** + * 字体设置 + * @see org.apache.poi.xssf.usermodel.XSSFFont + * @see org.apache.poi.hssf.usermodel.HSSFFont + */ + CellFontFormat cellFont() default @CellFontFormat(); + + + /** + * 背景颜色 + * @see IndexedColors + */ + IndexedColors fillBackgroundColor() default IndexedColors.WHITE; + + + /** + * 单元格是否换行 + */ + boolean warpText() default false; + +} \ No newline at end of file diff --git a/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/exception/ExcelException.java b/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/exception/ExcelException.java new file mode 100644 index 000000000..8e11493cf --- /dev/null +++ b/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/exception/ExcelException.java @@ -0,0 +1,21 @@ +package com.pig4cloud.common.excel.exception; + +/** + * Excel异常类 + * + * @author lishangbu + * @date 2019-06-11 + */ +public class ExcelException extends RuntimeException { + /** + * Constructs a new exception with the specified detail message. The + * cause is not initialized, and may subsequently be initialized by + * a call to {@link #initCause}. + * + * @param message the detail message. The detail message is saved for + * later retrieval by the {@link #getMessage()} method. + */ + public ExcelException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/factory/ExcelWriterFactory.java b/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/factory/ExcelWriterFactory.java new file mode 100644 index 000000000..e982bdaf7 --- /dev/null +++ b/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/factory/ExcelWriterFactory.java @@ -0,0 +1,63 @@ +package com.pig4cloud.common.excel.factory; + +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.metadata.BaseRowModel; +import com.alibaba.excel.metadata.Sheet; +import com.alibaba.excel.support.ExcelTypeEnum; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; + +/** + * excel工厂 + * + * @author lishangbu + * @date 2019-06-11 + */ +public class ExcelWriterFactory extends ExcelWriter { + private OutputStream outputStream; + private int sheetNo = 1; + + public ExcelWriterFactory(OutputStream outputStream, ExcelTypeEnum typeEnum) { + super(outputStream, typeEnum); + this.outputStream = outputStream; + } + + public ExcelWriterFactory write(List list, String sheetName, + BaseRowModel object) { + this.sheetNo++; + try { + Sheet sheet = new Sheet(sheetNo, 0, object.getClass()); + sheet.setSheetName(sheetName); + this.write(list, sheet); + } catch (Exception ex) { + ex.printStackTrace(); + try { + outputStream.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return this; + } + + @Override + public void finish() { + super.finish(); + try { + outputStream.flush(); + } catch (IOException e) { + e.printStackTrace(); + //do something + } + } + + public void close() { + try { + outputStream.close(); + } catch (IOException e) { + //do something + } + } +} diff --git a/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/handler/PreWriterHandler.java b/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/handler/PreWriterHandler.java new file mode 100644 index 000000000..b7a6f8897 --- /dev/null +++ b/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/handler/PreWriterHandler.java @@ -0,0 +1,116 @@ +package com.pig4cloud.common.excel.handler; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.event.WriteHandler; +import com.alibaba.excel.metadata.BaseRowModel; +import com.pig4cloud.common.excel.annotation.CellStyleFormat; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.xssf.usermodel.XSSFFont; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * excel写入预处理器,支持生成Excel2007版本样式 + * 参考 https://github.com/alibaba/easyexcel/issues/205 + * + * @author lishangbu + * @date 2019-06-12 + */ +public class PreWriterHandler> implements WriteHandler { + + private T rowModel; + + private Map colStyleFormat = new HashMap<>(); + + private Map colStyle = new HashMap<>(); + + private Map infos = new HashMap<>(); + + public static final String TITLE_NUM = "title_num"; + + public PreWriterHandler(T rowModel) { + this.rowModel = rowModel; + init(rowModel); + } + + private void init(T rowModel) { + Field[] declaredFields = rowModel.getDeclaredFields(); + //查找有CellStyle注解和ExcelProperty注解的Field加入cellStyle + int titleNum = 0; + for (Field field : declaredFields) { + CellStyleFormat cellStyle = field.getAnnotation(CellStyleFormat.class); + ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); + if (excelProperty != null) { + titleNum =Math.max(excelProperty.value().length, titleNum); + } + if (cellStyle == null || excelProperty == null) { + continue; + } + colStyleFormat.put(excelProperty.index(), cellStyle); + + } + infos.put(TITLE_NUM, String.valueOf(titleNum)); + } + + @Override + public void sheet(int sheetNo, Sheet sheet) { + // 只初始化一次 + if (sheetNo == 1) { + colStyle = colStyleFormat.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, x -> { + CellStyle cellStyle = sheet.getWorkbook().createCellStyle(); + CellStyleFormat value = x.getValue(); + //居中方式 + cellStyle.setVerticalAlignment(value.verticalAlignment()); + cellStyle.setAlignment(value.horizontalalignment()); + // 设置边框 + cellStyle.setBorderBottom(value.borderStyle()); + cellStyle.setBorderLeft(value.borderStyle()); + cellStyle.setBorderRight(value.borderStyle()); + cellStyle.setBorderTop(value.borderStyle()); + cellStyle.setBottomBorderColor(value.borderColor().getIndex()); + cellStyle.setLeftBorderColor(value.borderColor().getIndex()); + cellStyle.setRightBorderColor(value.borderColor().getIndex()); + cellStyle.setTopBorderColor(value.borderColor().getIndex()); + // 设置字体 + XSSFFont font = (XSSFFont) sheet.getWorkbook().createFont(); + font.setFontName(value.cellFont().fontName()); + font.setColor(value.cellFont().fontColor().getIndex()); + font.setFontHeightInPoints(value.cellFont().fontHeightInPoints()); + cellStyle.setFont(font); + // 设置单元格是否换行 + cellStyle.setWrapText(value.warpText()); + // 背景颜色 + cellStyle.setFillBackgroundColor(value.fillBackgroundColor().getIndex()); + return cellStyle; + }, + (u, v) -> v)); + } + } + + @Override + public void row(int rowNum, Row row) { + + } + + @Override + public void cell(int cellNum, Cell cell) { + CellStyle cellStyle = colStyle.get(cellNum); + // 表头不操作 行序号是从0开始的 + if (cell.getRowIndex() <= Integer.valueOf(infos.get(TITLE_NUM))-1) { + return; + } + // 没有不操作 + if (cellStyle == null) { + return; + } + //设置居中方式 + cell.setCellStyle(cellStyle); + } +} \ No newline at end of file diff --git a/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/listener/ExcelListener.java b/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/listener/ExcelListener.java new file mode 100644 index 000000000..bf62162e2 --- /dev/null +++ b/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/listener/ExcelListener.java @@ -0,0 +1,77 @@ +package com.pig4cloud.common.excel.listener; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import lombok.Getter; +import lombok.Setter; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +/** + * Excel监听器 + * 可以根据需要,自定义处理获取到的数据 + * + * @author lishangbu + * @date 2019-06-11 + */ +public class ExcelListener extends AnalysisEventListener { + + /** + * 自定义用于暂时存储data,可以通过实例获取该值 + */ + @Setter + @Getter + private List dataList = new ArrayList<>(); + + /** + * 通过 AnalysisContext 对象还可以获取当前 sheet,当前行等数据 + */ + @Override + public void invoke(Object object, AnalysisContext context) { + // 数据存储到list,供批量处理,或后续自己业务逻辑处理。 + if (!checkObjAllFieldsIsNull(object)) { + dataList.add(object); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + //do something + } + + private static final String SERIAL_VERSION_UID = "serialVersionUID"; + + /** + * 判断对象中属性值是否全为空 + * + * @param object + * @return true为空false不为空 + */ + private static boolean checkObjAllFieldsIsNull(Object object) { + if (ObjectUtil.isNull(object)) { + return true; + } + try { + for (Field f : object.getClass().getDeclaredFields()) { + f.setAccessible(true); + //只校验带ExcelProperty注解的属性 + ExcelProperty property = f.getAnnotation(ExcelProperty.class); + if (ObjectUtil.isNull(property) || SERIAL_VERSION_UID.equals(f.getName())) { + continue; + } + if (ObjectUtil.isNotNull(f.get(object)) && StrUtil.isNotBlank(f.get(object).toString())) { + return false; + } + } + } catch (Exception e) { + //do something + } + return true; + } + +} \ No newline at end of file diff --git a/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/util/BeanCopyUtils.java b/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/util/BeanCopyUtils.java new file mode 100644 index 000000000..726b4c867 --- /dev/null +++ b/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/util/BeanCopyUtils.java @@ -0,0 +1,171 @@ +package com.pig4cloud.common.excel.util; + +import cn.hutool.core.date.DatePattern; +import org.springframework.beans.*; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.util.Assert; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * bean拷贝 + * + * @author lishangbu + * @date 2019-06-11 + */ +public class BeanCopyUtils { + + private final class DateToStringConverter implements Converter { + @Override + public String convert(LocalDateTime source) { + return source.format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)); + } + } + private static Object convertForProperty(Wrapper wrapper, Object object, Object value, String propertyName) + throws TypeMismatchException { + Object result; + if (wrapper == null) { + result = null; + } else { + wrapper.setWrappedInstance(object); + result = wrapper.getBeanWrapper().convertForProperty(value, propertyName); + } + return result; + } + + private static Object copyProperties(Object source, Object target) throws BeansException { + Wrapper wrapper = new BeanCopyUtils().new Wrapper(source); + copyProperties(wrapper, source, target); + return target; + } + + /** + * Copy the property values of the given source bean into the target bean. + *

+ * Note: The source and target classes do not have to match or even be derived from each other, as long as the properties match. Any bean + * properties that the source bean exposes but the target bean does not will silently be ignored. + *

+ * This is just a convenience method. For more complex transfer needs, consider using a full BeanWrapper. + * + * @param source + * the source bean + * @param target + * the target bean + * @throws BeansException + * if the copying failed + */ + private static void copyProperties(Wrapper wrapper, Object source, Object target) throws BeansException { + Assert.notNull(source, "Source must not be null"); + Assert.notNull(target, "Target must not be null"); + + Class actualEditable = target.getClass(); + PropertyDescriptor[] targetPds = BeanUtils.getPropertyDescriptors(actualEditable); + + for (PropertyDescriptor targetPd : targetPds) { + if (targetPd.getWriteMethod() != null) { + PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName()); + if ( sourcePd.getReadMethod() != null) { + try { + Method readMethod = sourcePd.getReadMethod(); + if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { + readMethod.setAccessible(true); + } + Object value = readMethod.invoke(source); + // 判断是否类型不一致 + if (value != null && !(targetPd.getPropertyType().isInstance(value))) { + // 数据转型 + value = convertForProperty(wrapper, target, value, targetPd.getName()); + } + Method writeMethod = targetPd.getWriteMethod(); + if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { + writeMethod.setAccessible(true); + } + writeMethod.invoke(target, value); + } catch (Exception ex) { + throw new FatalBeanException("Could not copy properties from source to target", ex); + } + } + } + } + + } + + private final class Wrapper { + + private GenericConversionService conversion; + private BeanWrapperImpl bean; + + private Wrapper(Object object) { + conversion = initDefaultConversionService(); + bean = initDefaultBeanWrapper(conversion, object); + } + + private void setWrappedInstance(Object object) { + bean.setWrappedInstance(object); + } + + private GenericConversionService initDefaultConversionService() { + GenericConversionService conversionService = new DefaultConversionService(); + conversionService.addConverter(new DateToStringConverter()); + return conversionService; + } + + private BeanWrapperImpl initDefaultBeanWrapper(@SuppressWarnings("hiding") ConversionService conversion, + Object object) { + BeanWrapperImpl beanWrapper = new BeanWrapperImpl(object); + beanWrapper.setConversionService(conversion); + return beanWrapper; + } + + private BeanWrapperImpl getBeanWrapper() { + return bean; + } + } + + /** + * 复制源对象到目的对象 + */ + private static void convert(Object source, Object target) { + copyProperties(source, target); + } + + public static List convert(List sources, Class targetClass) { + List sourcesObj = sources; + if (sourcesObj == null) { + sourcesObj = Collections.emptyList(); + } + List targets = new ArrayList<>(sourcesObj.size()); + BeanCopyUtils.convert(sourcesObj, targets, targetClass); + return targets; + } + + private static void convert(List sources, List targets, Class targetClass) { + if (targets == null) { + return; + } + targets.clear(); + if (sources == null) { + return; + } + for (Object obj : sources) { + try { + T target = targetClass.newInstance(); + targets.add(target); + convert(obj, target); + } catch (Exception e) { + //do something + return; + } + } + } +} \ No newline at end of file diff --git a/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/util/ExcelUtils.java b/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/util/ExcelUtils.java new file mode 100644 index 000000000..c97f44130 --- /dev/null +++ b/pigx-common/pigx-common-excel/src/main/java/com/pig4cloud/common/excel/util/ExcelUtils.java @@ -0,0 +1,251 @@ +package com.pig4cloud.common.excel.util; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.excel.EasyExcelFactory; +import com.alibaba.excel.ExcelReader; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.metadata.BaseRowModel; +import com.alibaba.excel.metadata.Sheet; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.pig4cloud.common.excel.exception.ExcelException; +import com.pig4cloud.common.excel.factory.ExcelWriterFactory; +import com.pig4cloud.common.excel.handler.PreWriterHandler; +import com.pig4cloud.common.excel.listener.ExcelListener; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +/** + * Excel工具类 + * + * @author lishangbu + * @date 2019-06-11 + */ +@Slf4j +@UtilityClass +public class ExcelUtils { + + private final String DEFAULT_EXCEL_SHEET_NAME="Sheet1"; + + /** + * 读取 Excel(多个 sheet) + * 将多sheet合并成一个list数据集,通过自定义ExcelReader继承AnalysisEventListener + * 重写invoke doAfterAllAnalysed方法 + * getExtendsBeanList 主要是做Bean的属性拷贝 ,可以通过ExcelReader中添加的数据集直接获取 + * + * @param excel 文件 + * @param rowModel 实体类映射,继承 BaseRowModel 类 + * @return Excel 数据 list + */ + public List readExcel(MultipartFile excel, Class rowModel) { + ExcelListener excelListener = new ExcelListener(); + ExcelReader reader = getReader(excel, excelListener); + if (reader == null) { + return new ArrayList<>(); + } + for (Sheet sheet : reader.getSheets()) { + sheet.setClazz(rowModel); + reader.read(sheet); + } + return getExtendsBeanList(excelListener.getDataList(), rowModel); + } + + /** + * 读取某个 sheet 的 Excel + * + * @param excel 文件 + * @param rowModel 实体类映射,继承 BaseRowModel 类 + * @param sheetNo sheet 的序号 从1开始 + * @return Excel 数据 list + */ + public List readExcel(MultipartFile excel, Class rowModel, int sheetNo) { + return readExcel(excel, rowModel, sheetNo, 1); + } + + /** + * 读取某个 sheet 的 Excel + * + * @param excel 文件 + * @param rowModel 实体类映射,继承 BaseRowModel 类 + * @param sheetNo sheet 的序号 从1开始 + * @param headLineNum 表头行数,默认为1 + * @return Excel 数据 list + */ + public List readExcel(MultipartFile excel, Class rowModel, int sheetNo, + int headLineNum) { + ExcelListener excelListener = new ExcelListener(); + ExcelReader reader = getReader(excel, excelListener); + if (reader == null) { + return new ArrayList<>(); + } + reader.read(new Sheet(sheetNo, headLineNum, rowModel)); + return getExtendsBeanList(excelListener.getDataList(), rowModel); + } + + + /** + * 导出 2007 Excel :一个 sheet,带表头,默认sheet名称 + * + * @param response HttpServletResponse + * @param list 数据 list,每个元素为一个 BaseRowModel + * @param fileName 导出的文件名 + * @param classType 实体类映射,继承 BaseRowModel 类 + * @param + */ + public void writeExcel(HttpServletResponse response, List list, + String fileName, Class classType){ + writeExcel(response,list,fileName,null,classType); + } + + + /** + * 导出 2007 Excel :一个 sheet,带表头 + * + * @param response HttpServletResponse + * @param list 数据 list,每个元素为一个 BaseRowModel + * @param fileName 导出的文件名 + * @param sheetName 导入文件的 sheet 名 + * @param classType 实体类映射,继承 BaseRowModel 类 + * @param + */ + public void writeExcel(HttpServletResponse response, List list, + String fileName, String sheetName, + Class classType){ + writeExcel(response,list,fileName,sheetName,ExcelTypeEnum.XLSX,classType); + } + + /** + * 导出 Excel :一个 sheet,带表头 + * 自定义WriterHandler 可以定制行列数据进行灵活化操作 + * + * @param response HttpServletResponse + * @param list 数据 list,每个元素为一个 BaseRowModel + * @param fileName 导出的文件名 + * @param sheetName 导入文件的 sheet 名 + * @param excelTypeEnum 导出Excel类型 + * @param classType 实体类映射,继承 BaseRowModel 类 + * + */ + public void writeExcel(HttpServletResponse response, List list, + String fileName, String sheetName, ExcelTypeEnum excelTypeEnum, + Class classType) { + if (StrUtil.isBlank(sheetName)) { + sheetName = DEFAULT_EXCEL_SHEET_NAME; + } + OutputStream outputStream =null; + try { + response.reset(); + outputStream = getOutputStream(fileName, response, excelTypeEnum); + if (excelTypeEnum == ExcelTypeEnum.XLSX) { + ExcelWriter writer = EasyExcelFactory.getWriterWithTempAndHandler(null, outputStream + , excelTypeEnum, true, new PreWriterHandler<>(classType)); + Sheet sheet = new Sheet(1, 0, classType); + sheet.setSheetName(sheetName); + try { + writer.write(list, sheet); + } finally { + writer.finish(); + } + } else if (excelTypeEnum == ExcelTypeEnum.XLS) { + // 其实也可以专门调03版的样式,或者直接套用 + ExcelWriterFactory writer = new ExcelWriterFactory(outputStream, excelTypeEnum); + Sheet sheet = new Sheet(1, 0, classType); + sheet.setSheetName(sheetName); + try { + writer.write(list, sheet); + } finally { + writer.finish(); + writer.close(); + } + } + outputStream.flush(); + }catch (IOException e){ + log.error("导出excel异常:{}",e); + }finally { + if(outputStream!=null){ + try { + outputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + } + + /** + * 导出 Excel :多个 sheet,带表头 + * + * @param response HttpServletResponse + * @param list 数据 list,每个元素为一个 BaseRowModel + * @param fileName 导出的文件名 + * @param sheetName 导入文件的 sheet 名 + * @param object 映射实体类,Excel 模型 + */ + public ExcelWriterFactory writeExcelWithSheets(HttpServletResponse response, List list, + String fileName, String sheetName, BaseRowModel object, ExcelTypeEnum excelTypeEnum) { + ExcelWriterFactory writer = new ExcelWriterFactory(getOutputStream(fileName, response, excelTypeEnum), excelTypeEnum); + Sheet sheet = new Sheet(1, 0, object.getClass()); + sheet.setSheetName(sheetName); + writer.write(list, sheet); + return writer; + } + + /** + * 导出文件时为Writer生成OutputStream + */ + private OutputStream getOutputStream(String fileName, HttpServletResponse response, ExcelTypeEnum excelTypeEnum) { + try { + String encodeFilename = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()); + response.setContentType(MediaType.MULTIPART_FORM_DATA_VALUE); + response.setCharacterEncoding(StandardCharsets.UTF_8.name()); + response.setHeader("Content-disposition", "attachment;filename="+ encodeFilename+excelTypeEnum.getValue()); + return response.getOutputStream(); + } catch (IOException e) { + throw new ExcelException("创建文件失败!"); + } + } + + /** + * 返回 ExcelReader + * + * @param excel 需要解析的 Excel 文件 + * @param excelListener new ExcelListener() + */ + private ExcelReader getReader(MultipartFile excel, + ExcelListener excelListener) { + String fileName = excel.getOriginalFilename(); + if (fileName == null) { + throw new ExcelException("文件格式错误!"); + } + if (!fileName.toLowerCase().endsWith(ExcelTypeEnum.XLS.getValue()) && !fileName.toLowerCase().endsWith(ExcelTypeEnum.XLSX.getValue())) { + throw new ExcelException("文件格式错误!"); + } + InputStream inputStream; + try { + inputStream = excel.getInputStream(); + return new ExcelReader(inputStream, null, excelListener, false); + } catch (IOException e) { + //do something + } + return null; + } + + /** + * 利用BeanCopy转换list + */ + private List getExtendsBeanList(List list, Class typeClazz) { + return BeanCopyUtils.convert(list, typeClazz); + } +} + diff --git a/pigx-common/pom.xml b/pigx-common/pom.xml index 1227d7038..d8b988368 100644 --- a/pigx-common/pom.xml +++ b/pigx-common/pom.xml @@ -36,14 +36,15 @@ pigx-common-bom pigx-common-core pigx-common-data + pigx-common-datasource + pigx-common-excel pigx-common-gateway pigx-common-job pigx-common-log pigx-common-minio pigx-common-security - pigx-common-swagger pigx-common-sequence + pigx-common-swagger pigx-common-transaction - pigx-common-datasource diff --git a/pom.xml b/pom.xml index 93468c95a..60e8432ef 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,7 @@ 1.8 1.8 2.1.5 + 1.1.2-beta5 4.5.10 0.0.9 2.9.2