|
@@ -0,0 +1,450 @@
|
|
|
+package com.crunii.common.excel.utils;
|
|
|
+
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.hutool.core.collection.CollectionUtil;
|
|
|
+import cn.hutool.core.io.resource.ClassPathResource;
|
|
|
+import cn.hutool.core.util.IdUtil;
|
|
|
+import com.crunii.common.excel.core.DefaultExcelListener;
|
|
|
+import com.crunii.common.excel.dto.NoModelMultipleSheetsWriteData;
|
|
|
+import com.crunii.common.excel.dto.NoModelSheetData;
|
|
|
+import com.crunii.common.excel.dto.NoModelWriteData;
|
|
|
+import com.crunii.common.excel.core.CellMergeStrategy;
|
|
|
+import com.crunii.common.excel.core.ExcelListener;
|
|
|
+import com.alibaba.excel.EasyExcel;
|
|
|
+import com.alibaba.excel.ExcelWriter;
|
|
|
+import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
|
|
|
+import com.alibaba.excel.write.metadata.WriteSheet;
|
|
|
+import com.alibaba.excel.write.metadata.fill.FillConfig;
|
|
|
+import com.alibaba.excel.write.metadata.fill.FillWrapper;
|
|
|
+import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
|
|
|
+import com.crunii.common.excel.convert.ExcelBigNumberConvert;
|
|
|
+import com.crunii.common.excel.core.ExcelResult;
|
|
|
+import lombok.AccessLevel;
|
|
|
+import lombok.NoArgsConstructor;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+
|
|
|
+import javax.servlet.ServletOutputStream;
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.io.UnsupportedEncodingException;
|
|
|
+import java.net.URLEncoder;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Excel相关处理
|
|
|
+ *
|
|
|
+ * @author lsa
|
|
|
+ */
|
|
|
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
|
|
+public class ExcelUtil {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 同步导入(适用于小数据量)
|
|
|
+ *
|
|
|
+ * @param is 输入流
|
|
|
+ * @return 转换后集合
|
|
|
+ */
|
|
|
+ public static <T> List<T> importExcel(InputStream is, Class<T> clazz) {
|
|
|
+ return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 使用校验监听器 异步导入 同步返回
|
|
|
+ *
|
|
|
+ * @param is 输入流
|
|
|
+ * @param clazz 对象类型
|
|
|
+ * @param isValidate 是否 Validator 检验 默认为是
|
|
|
+ * @return 转换后集合
|
|
|
+ */
|
|
|
+ public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, boolean isValidate) {
|
|
|
+ DefaultExcelListener<T> listener = new DefaultExcelListener<>(isValidate);
|
|
|
+ EasyExcel.read(is, clazz, listener).sheet().doRead();
|
|
|
+ return listener.getExcelResult();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 使用自定义监听器 异步导入 自定义返回
|
|
|
+ *
|
|
|
+ * @param is 输入流
|
|
|
+ * @param clazz 对象类型
|
|
|
+ * @param listener 自定义监听器
|
|
|
+ * @return 转换后集合
|
|
|
+ */
|
|
|
+ public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, ExcelListener<T> listener) {
|
|
|
+ EasyExcel.read(is, clazz, listener).sheet().doRead();
|
|
|
+ return listener.getExcelResult();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出excel
|
|
|
+ *
|
|
|
+ * @param list 导出数据集合
|
|
|
+ * @param sheetName 工作表的名称
|
|
|
+ * @param clazz 实体类
|
|
|
+ * @param response 响应体
|
|
|
+ */
|
|
|
+ public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response) {
|
|
|
+ exportExcel(list, sheetName, clazz, false, response);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出excel
|
|
|
+ * @param list
|
|
|
+ * @param sheetName
|
|
|
+ * @param clazz
|
|
|
+ * @param excludeColumnFiledNames
|
|
|
+ * @param response
|
|
|
+ * @param <T>
|
|
|
+ */
|
|
|
+ public static <T> void exportExcelExclude(List<T> list, String sheetName, Class<T> clazz, Set<String> excludeColumnFiledNames, HttpServletResponse response) {
|
|
|
+ exportExcel(list, sheetName, clazz, false,excludeColumnFiledNames,null ,response);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出excel
|
|
|
+ * @param list
|
|
|
+ * @param sheetName
|
|
|
+ * @param clazz
|
|
|
+ * @param includeColumnFiledNames
|
|
|
+ * @param response
|
|
|
+ * @param <T>
|
|
|
+ */
|
|
|
+ public static <T> void exportExcelInclude(List<T> list, String sheetName, Class<T> clazz, Set<String> includeColumnFiledNames, HttpServletResponse response) {
|
|
|
+ exportExcel(list, sheetName, clazz, false,null,includeColumnFiledNames ,response);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出excel
|
|
|
+ *
|
|
|
+ * @param list 导出数据集合
|
|
|
+ * @param sheetName 工作表的名称
|
|
|
+ * @param clazz 实体类
|
|
|
+ * @param merge 是否合并单元格
|
|
|
+ * @param response 响应体
|
|
|
+ */
|
|
|
+ public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, HttpServletResponse response) {
|
|
|
+ try {
|
|
|
+ resetResponse(sheetName, response);
|
|
|
+ ServletOutputStream os = response.getOutputStream();
|
|
|
+ ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
|
|
|
+ .autoCloseStream(false)
|
|
|
+ // 自动适配
|
|
|
+ .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
|
|
|
+ // 大数值自动转换 防止失真
|
|
|
+ .registerConverter(new ExcelBigNumberConvert())
|
|
|
+ .sheet(sheetName);
|
|
|
+ if (merge) {
|
|
|
+ // 合并处理器
|
|
|
+ builder.registerWriteHandler(new CellMergeStrategy(list, true));
|
|
|
+ }
|
|
|
+ builder.doWrite(list);
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw new RuntimeException("导出Excel异常");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @description: 导出list<map<String,Object>>类型的数据
|
|
|
+ * @param
|
|
|
+ * @return:
|
|
|
+ * @author liuchuan
|
|
|
+ * @date: 2022/11/17 21:43
|
|
|
+ */
|
|
|
+ public static void noModleWrite(NoModelWriteData data, HttpServletResponse response) {
|
|
|
+ try {// response.setContentType("application/vnd.ms-excel");
|
|
|
+ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
|
|
+ response.setCharacterEncoding("utf-8");
|
|
|
+ // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
|
|
|
+ String fileName = URLEncoder.encode(data.getFileName(), "UTF-8");
|
|
|
+ response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
|
|
|
+ // 这里需要设置不关闭流
|
|
|
+ EasyExcel.write(response.getOutputStream()).head(head(data.getHeadMap())).sheet(data.getFileName()).
|
|
|
+ doWrite(dataList(data.getDataList(), data.getDataStrMap()));
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ throw new RuntimeException("导出Excel异常");
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 多sheet动态表头excel导出
|
|
|
+ *
|
|
|
+ * @param data 导出数据配置
|
|
|
+ * @param response http响应
|
|
|
+ */
|
|
|
+ public static void exportExcel(NoModelMultipleSheetsWriteData data, HttpServletResponse response) {
|
|
|
+ try {
|
|
|
+ resetResponse(data.getFileName(), response);
|
|
|
+ ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
|
|
|
+ .autoCloseStream(false)
|
|
|
+ // 大数值自动转换 防止失真
|
|
|
+ .registerConverter(new ExcelBigNumberConvert())
|
|
|
+ .build();
|
|
|
+ List<NoModelSheetData> sheetDataList = data.getSheetDataList();
|
|
|
+ for (int i = 0; i < sheetDataList.size(); i++) {
|
|
|
+ NoModelSheetData sheetData = sheetDataList.get(i);
|
|
|
+ //生成sheet页
|
|
|
+ WriteSheet sheet = EasyExcel.writerSheet(i, sheetData.getSheetName())
|
|
|
+ .head(head(sheetData.getHeadNameCn()))
|
|
|
+ .build();
|
|
|
+ //获取模型信息,向sheet写入数据
|
|
|
+ excelWriter.write(dataList(sheetData.getDataList(), sheetData.getHeadNameStr()), sheet);
|
|
|
+ }
|
|
|
+ excelWriter.finish();
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("导出Excel异常");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //设置表头
|
|
|
+ private static List<List<String>> head(String[] headMap) {
|
|
|
+ List<List<String>> list = new ArrayList<List<String>>();
|
|
|
+
|
|
|
+ for (String head : headMap) {
|
|
|
+ List<String> headList = new ArrayList<String>();
|
|
|
+ headList.add(head);
|
|
|
+ list.add(headList);
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ //设置导出的数据内容
|
|
|
+ private static List<List<Object>> dataList(List<Map<String, Object>> dataList, String[] dataStrMap) {
|
|
|
+ List<List<Object>> list = new ArrayList<List<Object>>();
|
|
|
+ for (Map<String, Object> map : dataList) {
|
|
|
+ List<Object> data = new ArrayList<Object>();
|
|
|
+ for (int i = 0; i < dataStrMap.length; i++) {
|
|
|
+ data.add(map.get(dataStrMap[i]));
|
|
|
+ }
|
|
|
+ list.add(data);
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void dynamicHeadWrite(HttpServletResponse response, List<String> names, String sheetName, String fileName){
|
|
|
+ try {
|
|
|
+
|
|
|
+ //设置返回数据的值跟动态列一一对应
|
|
|
+// List<List<String>> datas = setData(list,fieldEn);
|
|
|
+ resetResponse(sheetName, response);
|
|
|
+ EasyExcel.write(response.getOutputStream())
|
|
|
+ .head(headData(CollectionUtil.isNotEmpty(names) ? names.toArray(new String[0]) : new String[0]))
|
|
|
+ .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
|
|
|
+ .sheet(sheetName)
|
|
|
+ .doWrite(new ArrayList());
|
|
|
+
|
|
|
+ } catch (IOException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ response.reset();
|
|
|
+ response.setCharacterEncoding("utf-8");
|
|
|
+ response.setContentType("application/json");
|
|
|
+ try {
|
|
|
+ response.getWriter().println("打印失败");
|
|
|
+ } catch (IOException ex) {
|
|
|
+ ex.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 数据动态头传入
|
|
|
+ */
|
|
|
+ private static List<List<String>> headData(String[] header) {
|
|
|
+ List<String> head0 = null;
|
|
|
+ List<List<String>> list = new LinkedList<List<String>>();
|
|
|
+ for (String h : header) {
|
|
|
+ head0 = new LinkedList<>();
|
|
|
+ head0.add(h);
|
|
|
+ list.add(head0);
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出excel
|
|
|
+ * @param list
|
|
|
+ * @param sheetName
|
|
|
+ * @param clazz
|
|
|
+ * @param merge
|
|
|
+ * @param excludeColumnFiledNames
|
|
|
+ * @param includeColumnFiledNames
|
|
|
+ * @param response
|
|
|
+ * @param <T>
|
|
|
+ */
|
|
|
+ public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, Set<String> excludeColumnFiledNames,Set<String> includeColumnFiledNames,HttpServletResponse response) {
|
|
|
+ try {
|
|
|
+ resetResponse(sheetName, response);
|
|
|
+ ServletOutputStream os = response.getOutputStream();
|
|
|
+ ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
|
|
|
+ .autoCloseStream(false)
|
|
|
+ // 自动适配
|
|
|
+ .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
|
|
|
+ // 大数值自动转换 防止失真
|
|
|
+ .registerConverter(new ExcelBigNumberConvert())
|
|
|
+ .sheet(sheetName);
|
|
|
+ if (CollectionUtil.isNotEmpty(excludeColumnFiledNames)) {
|
|
|
+ builder.excludeColumnFieldNames(excludeColumnFiledNames);
|
|
|
+ }
|
|
|
+ if (CollectionUtil.isNotEmpty(includeColumnFiledNames)) {
|
|
|
+ builder.includeColumnFieldNames(includeColumnFiledNames);
|
|
|
+ }
|
|
|
+ if (merge) {
|
|
|
+ // 合并处理器
|
|
|
+ builder.registerWriteHandler(new CellMergeStrategy(list, true));
|
|
|
+ }
|
|
|
+ builder.doWrite(list);
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw new RuntimeException("导出Excel异常");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 单表多数据模板导出 模板格式为 {.属性}
|
|
|
+ *
|
|
|
+ * @param filename 文件名
|
|
|
+ * @param templatePath 模板路径 resource 目录下的路径包括模板文件名
|
|
|
+ * 例如: excel/temp.xlsx
|
|
|
+ * 重点: 模板文件必须放置到启动类对应的 resource 目录下
|
|
|
+ * @param data 模板需要的数据
|
|
|
+ */
|
|
|
+ public static void exportTemplate(List<Object> data, String filename, String templatePath, HttpServletResponse response) {
|
|
|
+ try {
|
|
|
+ resetResponse(filename, response);
|
|
|
+ ClassPathResource templateResource = new ClassPathResource(templatePath);
|
|
|
+ ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
|
|
|
+ .withTemplate(templateResource.getStream())
|
|
|
+ .autoCloseStream(false)
|
|
|
+ // 大数值自动转换 防止失真
|
|
|
+ .registerConverter(new ExcelBigNumberConvert())
|
|
|
+ .build();
|
|
|
+ WriteSheet writeSheet = EasyExcel.writerSheet().build();
|
|
|
+ if (CollUtil.isEmpty(data)) {
|
|
|
+ throw new IllegalArgumentException("数据为空");
|
|
|
+ }
|
|
|
+ // 单表多数据导出 模板格式为 {.属性}
|
|
|
+ for (Object d : data) {
|
|
|
+ excelWriter.fill(d, writeSheet);
|
|
|
+ }
|
|
|
+ excelWriter.finish();
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw new RuntimeException("导出Excel异常");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 多表多数据模板导出 模板格式为 {key.属性}
|
|
|
+ *
|
|
|
+ * @param filename 文件名
|
|
|
+ * @param templatePath 模板路径 resource 目录下的路径包括模板文件名
|
|
|
+ * 例如: excel/temp.xlsx
|
|
|
+ * 重点: 模板文件必须放置到启动类对应的 resource 目录下
|
|
|
+ * @param data 模板需要的数据
|
|
|
+ */
|
|
|
+ public static void exportTemplateMultiList(Map<String, Object> data, String filename, String templatePath, HttpServletResponse response) {
|
|
|
+ try {
|
|
|
+ resetResponse(filename, response);
|
|
|
+ ClassPathResource templateResource = new ClassPathResource(templatePath);
|
|
|
+ ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
|
|
|
+ .withTemplate(templateResource.getStream())
|
|
|
+ .autoCloseStream(false)
|
|
|
+ // 大数值自动转换 防止失真
|
|
|
+ .registerConverter(new ExcelBigNumberConvert())
|
|
|
+ .build();
|
|
|
+ WriteSheet writeSheet = EasyExcel.writerSheet().build();
|
|
|
+ if (CollUtil.isEmpty(data)) {
|
|
|
+ throw new IllegalArgumentException("数据为空");
|
|
|
+ }
|
|
|
+ for (Map.Entry<String, Object> map : data.entrySet()) {
|
|
|
+ // 设置列表后续还有数据
|
|
|
+ FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
|
|
|
+ if (map.getValue() instanceof Collection) {
|
|
|
+ // 多表导出必须使用 FillWrapper
|
|
|
+ excelWriter.fill(new FillWrapper(map.getKey(), (Collection<?>) map.getValue()), fillConfig, writeSheet);
|
|
|
+ } else {
|
|
|
+ excelWriter.fill(map.getValue(), writeSheet);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ excelWriter.finish();
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw new RuntimeException("导出Excel异常");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 重置响应体
|
|
|
+ */
|
|
|
+ private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException {
|
|
|
+ String filename = encodingFilename(sheetName);
|
|
|
+ response.reset();
|
|
|
+ FileUtils.setAttachmentResponseHeader(response, filename);
|
|
|
+ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析导出值 0=男,1=女,2=未知
|
|
|
+ *
|
|
|
+ * @param propertyValue 参数值
|
|
|
+ * @param converterExp 翻译注解
|
|
|
+ * @param separator 分隔符
|
|
|
+ * @return 解析后值
|
|
|
+ */
|
|
|
+ public static String convertByExp(String propertyValue, String converterExp, String separator) {
|
|
|
+ StringBuilder propertyString = new StringBuilder();
|
|
|
+ String[] convertSource = converterExp.split(",");
|
|
|
+ for (String item : convertSource) {
|
|
|
+ String[] itemArray = item.split("=");
|
|
|
+ if (StringUtils.containsAny(propertyValue, separator)) {
|
|
|
+ for (String value : propertyValue.split(separator)) {
|
|
|
+ if (itemArray[0].equals(value)) {
|
|
|
+ propertyString.append(itemArray[1] + separator);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (itemArray[0].equals(propertyValue)) {
|
|
|
+ return itemArray[1];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return StringUtils.stripEnd(propertyString.toString(), separator);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 反向解析值 男=0,女=1,未知=2
|
|
|
+ *
|
|
|
+ * @param propertyValue 参数值
|
|
|
+ * @param converterExp 翻译注解
|
|
|
+ * @param separator 分隔符
|
|
|
+ * @return 解析后值
|
|
|
+ */
|
|
|
+ public static String reverseByExp(String propertyValue, String converterExp, String separator) {
|
|
|
+ StringBuilder propertyString = new StringBuilder();
|
|
|
+ String[] convertSource = converterExp.split(",");
|
|
|
+ for (String item : convertSource) {
|
|
|
+ String[] itemArray = item.split("=");
|
|
|
+ if (StringUtils.containsAny(propertyValue, separator)) {
|
|
|
+ for (String value : propertyValue.split(separator)) {
|
|
|
+ if (itemArray[1].equals(value)) {
|
|
|
+ propertyString.append(itemArray[0] + separator);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (itemArray[1].equals(propertyValue)) {
|
|
|
+ return itemArray[0];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return StringUtils.stripEnd(propertyString.toString(), separator);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 编码文件名
|
|
|
+ */
|
|
|
+ public static String encodingFilename(String filename) {
|
|
|
+ return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx";
|
|
|
+ }
|
|
|
+
|
|
|
+}
|