package com.dl.common.utils.poi; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.resource.ClassPathResource; import cn.hutool.core.util.IdUtil; 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.dl.common.convert.ExcelBigNumberConvert; import com.dl.common.excel.CellMergeStrategy; import com.dl.common.excel.DefaultExcelListener; import com.dl.common.excel.ExcelListener; import com.dl.common.excel.ExcelResult; import com.dl.common.utils.StringUtils; import com.dl.common.utils.file.FileUtils; import lombok.AccessLevel; import lombok.NoArgsConstructor; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.List; import java.util.Map; /** * Excel相关处理 * * @author Lion Li */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ExcelUtil { /** * 同步导入(适用于小数据量) * * @param is 输入流 * @return 转换后集合 */ public static List importExcel(InputStream is, Class clazz) { return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync(); } /** * 使用校验监听器 异步导入 同步返回 * * @param is 输入流 * @param clazz 对象类型 * @param isValidate 是否 Validator 检验 默认为是 * @return 转换后集合 */ public static ExcelResult importExcel(InputStream is, Class clazz, boolean isValidate) { DefaultExcelListener listener = new DefaultExcelListener<>(isValidate); EasyExcel.read(is, clazz, listener).sheet().doRead(); return listener.getExcelResult(); } /** * 使用自定义监听器 异步导入 自定义返回 * * @param is 输入流 * @param clazz 对象类型 * @param listener 自定义监听器 * @return 转换后集合 */ public static ExcelResult importExcel(InputStream is, Class clazz, ExcelListener listener) { EasyExcel.read(is, clazz, listener).sheet().doRead(); return listener.getExcelResult(); } /** * 导出excel * * @param list 导出数据集合 * @param sheetName 工作表的名称 * @param clazz 实体类 * @param response 响应体 */ public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response) { try { resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); exportExcel(list, sheetName, clazz, false, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } } /** * 导出excel * * @param list 导出数据集合 * @param sheetName 工作表的名称 * @param clazz 实体类 * @param merge 是否合并单元格 * @param response 响应体 */ public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, HttpServletResponse response) { try { resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); exportExcel(list, sheetName, clazz, merge, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } } /** * 导出excel * * @param list 导出数据集合 * @param sheetName 工作表的名称 * @param clazz 实体类 * @param os 输出流 */ public static void exportExcel(List list, String sheetName, Class clazz, OutputStream os) { exportExcel(list, sheetName, clazz, false, os); } /** * 导出excel * * @param list 导出数据集合 * @param sheetName 工作表的名称 * @param clazz 实体类 * @param merge 是否合并单元格 * @param os 输出流 */ public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, OutputStream os) { 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); } /** * 单表多数据模板导出 模板格式为 {.属性} * * @param filename 文件名 * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 * 例如: excel/temp.xlsx * 重点: 模板文件必须放置到启动类对应的 resource 目录下 * @param data 模板需要的数据 * @param response 响应体 */ public static void exportTemplate(List data, String filename, String templatePath, HttpServletResponse response) { try { resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); exportTemplate(data, templatePath, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } } /** * 单表多数据模板导出 模板格式为 {.属性} * * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 * 例如: excel/temp.xlsx * 重点: 模板文件必须放置到启动类对应的 resource 目录下 * @param data 模板需要的数据 * @param os 输出流 */ public static void exportTemplate(List data, String templatePath, OutputStream os) { ClassPathResource templateResource = new ClassPathResource(templatePath); ExcelWriter excelWriter = EasyExcel.write(os) .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(); } /** * 多表多数据模板导出 模板格式为 {key.属性} * * @param filename 文件名 * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 * 例如: excel/temp.xlsx * 重点: 模板文件必须放置到启动类对应的 resource 目录下 * @param data 模板需要的数据 * @param response 响应体 */ public static void exportTemplateMultiList(Map data, String filename, String templatePath, HttpServletResponse response) { try { resetResponse(filename, response); ServletOutputStream os = response.getOutputStream(); exportTemplateMultiList(data, templatePath, os); } catch (IOException e) { throw new RuntimeException("导出Excel异常"); } } /** * 多表多数据模板导出 模板格式为 {key.属性} * * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 * 例如: excel/temp.xlsx * 重点: 模板文件必须放置到启动类对应的 resource 目录下 * @param data 模板需要的数据 * @param os 输出流 */ public static void exportTemplateMultiList(Map data, String templatePath, OutputStream os) { ClassPathResource templateResource = new ClassPathResource(templatePath); ExcelWriter excelWriter = EasyExcel.write(os) .withTemplate(templateResource.getStream()) .autoCloseStream(false) // 大数值自动转换 防止失真 .registerConverter(new ExcelBigNumberConvert()) .build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); if (CollUtil.isEmpty(data)) { throw new IllegalArgumentException("数据为空"); } for (Map.Entry 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(); } /** * 重置响应体 */ private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException { String filename = encodingFilename(sheetName); 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(StringUtils.SEPARATOR); 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(StringUtils.SEPARATOR); 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"; } }