| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- package com.malk.utils;
- import cn.hutool.core.util.ObjectUtil;
- import com.alibaba.excel.EasyExcel;
- import com.alibaba.excel.ExcelWriter;
- import com.alibaba.excel.write.metadata.WriteSheet;
- import com.google.common.base.Strings;
- import com.malk.server.common.McException;
- import com.malk.server.common.McREnum;
- import lombok.Builder;
- import lombok.Data;
- import lombok.SneakyThrows;
- import org.apache.commons.lang3.StringUtils;
- import org.apache.poi.ss.usermodel.BorderStyle;
- import org.apache.poi.xssf.usermodel.*;
- import javax.annotation.Nullable;
- import javax.servlet.http.HttpServletResponse;
- import javax.validation.constraints.NotNull;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.math.BigDecimal;
- import java.net.URLEncoder;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.List;
- import java.util.Map;
- /**
- * excel 导出工具 [poi & easy excel]
- */
- @Builder
- @Data
- public class UtilExcel {
- /**
- * 设置响应流
- */
- @SneakyThrows
- public static void setResponseHeader(HttpServletResponse response, @NotNull String fileName, String extension) {
- // 后缀: poi, xlsx下预览无详情; EasyExcel 需要使用xlsx, 否则不能预览
- if (StringUtils.isBlank(extension)) {
- extension = ".xls";
- }
- String date = new SimpleDateFormat("yyyy-MM-dd HH_mm_ss").format(new Date());
- fileName = fileName + "_" + date + extension;
- // 设置导出流信息
- response.setContentType("application/vnd.ms-excel;charset=UTF-8");
- response.setCharacterEncoding("utf-8");
- // 文件名兼容中文
- fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
- response.setHeader("Content-Disposition", "attachment;filename*=utf-8" + fileName);
- // 想要让客户端CellStyleModel可以访问到其他的首部信息,服务器不仅要在header里加入该首部,还要将它们在 Access-Control-Expose-Headers 里面列出来
- response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
- }
- /////////////////// Poi ///////////////////
- // 各个列的表头
- private String[] heardList;
- // 各个列的元素key值
- private String[] heardKey;
- // 需要填充的数据信息
- private List<Map> data;
- // 导出文件名称
- private String fileName;
- // 字体大小
- @Builder.Default
- private int fontSize = 12;
- @Builder.Default
- private String fontName = "微软雅黑";
- // 行高
- @Builder.Default
- private int rowHeight = 30;
- // 列宽
- @Builder.Default
- private int columnWidth = 20;
- // 工作表
- @Builder.Default
- private String sheetName = "sheet1";
- /**
- * 回调单元格样式
- * -
- * cellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); // 是设置前景色不是背景色
- * cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
- */
- @FunctionalInterface
- public interface UpdateCellStyle {
- void invoke(String value, XSSFCellStyle cellStyle);
- }
- public void exportExcelByPoi(HttpServletResponse response) {
- exportExcelByPoi(response, null);
- }
- /**
- * Poi 导出功能
- * -
- * - java中的强制类型转换只是针对单个对象的,想要偷懒将整个数组转换成另外一种类型的数组是不行的. toArray 运行中已经转为 Object, 需要指定初始化类型
- * - 4.x打包引用会异常: HSSFCellStyle.BORDER_THIN 指向的是 CellStyle.BORDER_THIN. 另若引入 easyExcel, 无需再引入 poi [其依赖是低版本 poi]
- * - 数量限制: HSSF改为XSSF后,导出1000000条数据. HSSF最大容量为65535 [最大65536(2的16次方)行;即横向256个单元格,竖向65536个单元格]
- */
- public void exportExcelByPoi(HttpServletResponse response, UpdateCellStyle lambda) {
- // 检查参数配置信息
- checkConfig();
- // 创建工作簿
- XSSFWorkbook wb = new XSSFWorkbook();
- // 创建工作表
- XSSFSheet wbSheet = wb.createSheet(sheetName);
- // 设置默认行宽
- wbSheet.setDefaultColumnWidth(columnWidth);
- // 设置表格样式
- XSSFCellStyle styleHeader = createCellStyle(wb);
- // 设置表头字体
- XSSFFont fontHeader = wb.createFont();
- fontHeader.setFontHeightInPoints((short) fontSize);
- fontHeader.setFontName(fontName);
- fontHeader.setBold(true);
- styleHeader.setFont(fontHeader);
- //设置列头元素
- XSSFRow row = wbSheet.createRow(0);
- XSSFCell cellHead = null;
- for (int i = 0; i < heardList.length; i++) {
- cellHead = row.createCell(i);
- cellHead.setCellValue(heardList[i]);
- cellHead.setCellStyle(styleHeader);
- }
- // 设置表格样式
- XSSFCellStyle bodyStyle = createCellStyle(wb);
- // 设置表格字体
- XSSFFont fontBody = wb.createFont();
- fontBody.setFontHeightInPoints((short) fontSize);
- fontBody.setFontName(fontName);
- fontBody.setBold(false);
- bodyStyle.setFont(fontBody);
- //开始写入实体数据信息
- int a = 1;
- for (int i = 0; i < data.size(); i++) {
- XSSFRow roww = wbSheet.createRow(a);
- Map map = data.get(i);
- XSSFCell cell = null;
- for (int j = 0; j < heardKey.length; j++) {
- cell = roww.createCell(j);
- cell.setCellStyle(bodyStyle);
- Object valueObject = map.get(heardKey[j]);
- String value = null;
- if (valueObject == null) {
- valueObject = "";
- }
- if (valueObject instanceof String) {
- //取出的数据是字符串直接赋值
- value = (String) map.get(heardKey[j]);
- } else if (valueObject instanceof Integer) {
- //取出的数据是Integer
- value = String.valueOf(((Integer) (valueObject)).floatValue());
- } else if (valueObject instanceof BigDecimal) {
- //取出的数据是BigDecimal
- value = String.valueOf(((BigDecimal) (valueObject)).floatValue());
- } else {
- value = valueObject.toString();
- }
- cell.setCellValue(Strings.isNullOrEmpty(value) ? "" : value);
- // 独立设置单元格样式
- if (ObjectUtil.isNotNull(lambda)) {
- XSSFCellStyle newBodyStyle = createCellStyle(wb);
- newBodyStyle.setFont(fontBody);
- lambda.invoke(value, newBodyStyle);
- cell.setCellStyle(newBodyStyle);
- }
- }
- a++;
- }
- // 导出文件
- setResponseHeader(response, fileName, ".xls");
- try {
- // 取得输出流
- OutputStream os = response.getOutputStream();
- wb.write(os);
- os.flush();
- os.close();
- } catch (IOException ex) {
- throw new McException(McREnum.METHOD_EXECUTE);
- }
- }
- // 不同的 CellStyle 需要独立创建
- private XSSFCellStyle createCellStyle(XSSFWorkbook wb) {
- XSSFCellStyle style = wb.createCellStyle();
- style.setBorderBottom(BorderStyle.THIN); //下边框
- style.setBorderLeft(BorderStyle.THIN); //左边框
- style.setBorderTop(BorderStyle.THIN); //上边框
- style.setBorderRight(BorderStyle.THIN); //右边框
- return style;
- }
- // 检查数据配置问题
- protected void checkConfig() {
- if (heardKey == null || heardList.length == 0) {
- McException.exceptionParam("列名数组不能为空或者为NULL");
- }
- if (fontSize < 0 || rowHeight < 0 || columnWidth < 0) {
- McException.exceptionParam("字体、宽度或者高度不能为负值");
- }
- if (Strings.isNullOrEmpty(fileName)) {
- McException.exceptionParam("导出文件名称不能为NULL");
- }
- }
- /////////////////// EasyExcel ///////////////////
- /**
- * 导出功能 EasyExcel [class支持map]
- * -
- * - Mac下, 若报错 Times 字体找不到, 下载安装一下, 不影响功能使用
- * - ClassPathResource, 需要打包/编译后才能访问到. 识别不是架包内内容
- */
- @SneakyThrows
- public static void exportListByTemplate(HttpServletResponse response, List dataList, Class dtoClass, @Nullable String fileName, String templateName) {
- InputStream inputStream = UtilFile.readPackageResource("templates/" + templateName);
- UtilExcel.setResponseHeader(response, fileName, ".xlsx");
- // 模板导出
- EasyExcel.write(response.getOutputStream(), dtoClass).withTemplate(inputStream).sheet().doFill(dataList);
- }
- /**
- * 列表与主表进行填充 [格式: 模板主表 {字段}, 列表 {.字段}]
- */
- @SneakyThrows
- public static void exportMapAndListByTemplate(HttpServletResponse response, Object dataMain, List dataList, Class dtoClass, @Nullable String fileName, String templateName) {
- InputStream inputStream = UtilFile.readPackageResource("templates/" + templateName);
- UtilExcel.setResponseHeader(response, fileName, ".xlsx");
- ExcelWriter workBook = EasyExcel.write(response.getOutputStream(), dtoClass).withTemplate(inputStream).build();
- WriteSheet sheet = EasyExcel.writerSheet("HL2025001").build();
- // 先单组数据填充,再多组数据填充
- workBook.fill(dataMain, sheet);
- workBook.fill(dataList, sheet);
- workBook.finish();
- }
- /**
- * 列表与主表进行填充 [格式: 模板主表 {字段}, 列表 {.字段}]
- */
- @SneakyThrows
- public static void exportMapAndListByTemplate(HttpServletResponse response, List dataList, Class dtoClass, @Nullable String fileName, String templateName) {
- InputStream inputStream = UtilFile.readPackageResource("templates/" + templateName);
- UtilExcel.setResponseHeader(response, fileName, ".xlsx");
- ExcelWriter workBook = EasyExcel.write(response.getOutputStream(), dtoClass).withTemplate(inputStream).build();
- WriteSheet sheet = EasyExcel.writerSheet().build();
- // 先单组数据填充,再多组数据填充
- workBook.fill(dataList, sheet);
- workBook.finish();
- }
- }
|