java自定义Excel导出实现方案汇总

涉及依赖

com.alibaba.excel.EasyExcel
org.apache.poi
cn.hutool.core.io.resource

通用EasyExcel工具导出方法

public void exportFile(
      HttpServletResponse response,
      String fileName,
      List<List<String>> list,
      Map<Integer, String[]> mapDropDown,
      List<List<Object>> filterTableList) {
    try {
      response.setContentType("multipart/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
      response.setCharacterEncoding("utf-8");
      String exportFileName = URLEncoder.encode(fileName, "UTF-8");

      response.setHeader("Content-disposition", "attachment;filename=" + exportFileName + ".xlsx");
      EasyExcel.write(response.getOutputStream())
      //设置自定义表头标题
          .head(list)
          // 设置自定义样式(例如下拉显示)
          .registerWriteHandler(new CustomSheetWriteHandler(mapDropDown))
          .sheet("sheet1")
          // 设置默认样式,单元格需要自定义样式这里需要设置为false
          .useDefaultStyle(true)
          //表格数据
          .doWrite(filterTableList);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

自定义单行表头内容

一、非固定表头标题属性名称类型可以保存在json文件,json文件获取属性名称,数据类型等自定义特殊处理。
例子:

{
  "fileName": "XXX表",
  "propertys": [
    {
      "headerName": "序号",
      "propertyName": "",
      "type": "INT",
      "isLov": false,
      "lovInfoName": ""
    },
    {
      "headerName": "所有人",
      "propertyName": "owner",
      "type": "STRING",
      "isLovInfo": false,
      "lovInfoName": ""
    }
   ]
  }

二、读取json获取表头

 List<String> mustPropertyList = new ArrayList<>();
   
 JSONObject dataProperty =
            JSONObject.parseObject(ResourceUtil.readUtf8Str("productProperty.json"));
 String fileName = dataProperty.getString("fileName");
 List<JSONObject> properties =
            dataProperty.getJSONArray("propertys").toJavaList(JSONObject.class);
        if (properties == null || properties.size() < 1) {
          return;
        }
  // 存表头
        for (int i = 0; i < properties.size(); i++) {
          JSONObject property = properties.get(i);
          mustPropertyList.add(property.getString("headerName"));
          }
     // 表头横向展示
        List<List<String>> list = new ArrayList<>();
        for (String headItem : mustPropertyList) {
          List<String> headList = new ArrayList<>();
          headList.add(headItem);
          list.add(headList);
        }     

注意表头list的保存方式,每一列在内层list内,合并单元格,每个单元格填入相同表头值,相同行或相同列会自动合并

自定义多级表头内容

其他处理同上

// 存表头
//三级表头标题(json文件保存)
      for (int i = 0; i < properties.size(); i++) {
        JSONObject property = properties.get(i);
        mustPropertyList.add(property.getString("headerName"));
      }
      // 设置标题
      Date now = new Date();
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
      String dateStr = sdf.format(now);
      // 首行含日期表头居中
      String bigTitle = "xxx           xxx表          " + dateStr;
      List<List<String>> headList = new ArrayList<>();
      List<Integer> imageColumnIndex = new ArrayList<>();
      for (int j = 0; j < secondTitle.size(); j++) {
        imageColumnIndex.add(j * mustPropertyList.size() + 1);
        for (int k = 0; k < mustPropertyList.size(); k++) {
          List<String> head = new ArrayList<>();
          //首行
          head.add(bigTitle);
          //第二行
          head.add(secondTitle.get(j));
          //第三行
          head.add(mustPropertyList.get(k));
          if (0 == k) {
          //合并上下行
            head.add("序号");
          } else {
            head.add(mustPropertyList.get(k));
          }
          headList.add(head);
        }
      }

导出表头样例
在这里插入图片描述

自定义表头样式

package xxx;

import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @description 自定义单元格样式
 */
public class CustomImageCellWriteHandler implements CellWriteHandler {

  @Override
  public void beforeCellCreate(
      WriteSheetHolder writeSheetHolder,
      WriteTableHolder writeTableHolder,
      Row row,
      Head head,
      Integer columnIndex,
      Integer relativeRowIndex,
      Boolean isHead) {}

  @Override
  public void afterCellCreate(
      WriteSheetHolder writeSheetHolder,
      WriteTableHolder writeTableHolder,
      Cell cell,
      Head head,
      Integer relativeRowIndex,
      Boolean isHead) {}

  @Override
  public void afterCellDataConverted(
      WriteSheetHolder writeSheetHolder,
      WriteTableHolder writeTableHolder,
      WriteCellData<?> cellData,
      Cell cell,
      Head head,
      Integer relativeRowIndex,
      Boolean isHead) {
  }

  @Override
  public void afterCellDispose(
      WriteSheetHolder writeSheetHolder,
      WriteTableHolder writeTableHolder,
      List<WriteCellData<?>> cellDataList,
      Cell cell,
      Head head,
      Integer relativeRowIndex,
      Boolean isHead) {
    Sheet sheet = cell.getSheet();
    if (isHead) {
      Row row = cell.getRow();
      // 设置标题样式
      XSSFCellStyle cellStyle =
          (XSSFCellStyle) cell.getRow().getSheet().getWorkbook().createCellStyle();
      cellStyle.cloneStyleFrom(cell.getCellStyle());
      cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
      cellStyle.setAlignment(HorizontalAlignment.CENTER);
      //单元格背景色
     cellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
      cellStyle.setFillBackgroundColor(IndexedColors.WHITE.getIndex());
      // 单元格宽度自适应
      cellStyle.setShrinkToFit(true);
      // 边框
      cellStyle.setBorderBottom(BorderStyle.THIN); // 下边框
      cellStyle.setBorderLeft(BorderStyle.THIN); // 左边框
      cellStyle.setBorderTop(BorderStyle.THIN); // 上边框
      cellStyle.setBorderRight(BorderStyle.THIN); // 右边框
      // 单元格边框颜色
      cellStyle.setLeftBorderColor(IndexedColors.BLACK.index);
      cellStyle.setRightBorderColor(IndexedColors.BLACK.index);
      cellStyle.setTopBorderColor(IndexedColors.BLACK.index);
      cellStyle.setBottomBorderColor(IndexedColors.BLACK.index);
      // 设置字体
      XSSFFont font = (XSSFFont) cell.getRow().getSheet().getWorkbook().createFont();
      font.setFontName("微软雅黑");
      font.setBold(true);

      if (0 == cell.getRowIndex()) {
        font.setFontHeightInPoints((short) 20);
      } else if (1 == cell.getRowIndex()) {
        font.setFontHeightInPoints((short) 11);
      } else {
        font.setFontHeightInPoints((short) 8);
      }
      cellStyle.setFont(font);
      cell.setCellStyle(cellStyle);
    } else {
      XSSFCellStyle cellStyle =
          (XSSFCellStyle) cell.getRow().getSheet().getWorkbook().createCellStyle();
      cellStyle.cloneStyleFrom(cell.getCellStyle());
      cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
      cellStyle.setAlignment(HorizontalAlignment.CENTER);
      // 自动换行
      cellStyle.setWrapText(true);
      // 边框
      cellStyle.setBorderBottom(BorderStyle.THIN); // 下边框
      cellStyle.setBorderLeft(BorderStyle.THIN); // 左边框
      cellStyle.setBorderTop(BorderStyle.THIN); // 上边框
      cellStyle.setBorderRight(BorderStyle.THIN); // 右边框
      // 设置字体
      XSSFFont font = (XSSFFont) cell.getRow().getSheet().getWorkbook().createFont();
      font.setFontName("微软雅黑");
      font.setFontHeightInPoints((short) 9);
      cellStyle.setFont(font);
      cell.setCellStyle(cellStyle);
    }
  }

  
}

自定义多sheet页的单元格样式

拦截器设置单元格样式
package xxx;

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.util.StyleUtil;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.fastjson.JSONObject;
import java.util.List;
import java.util.Map;
import org.apache.poi.ss.usermodel.*;

/**
 * @Descpription 自定义单元格样式
 */
public class CellStyleSheetWriteHandler implements CellWriteHandler {

  /** map key:第i行 value:第二个map的key表示第i列,JSONObject保存样式,值(可以自行修改补充) */
  private Map<Integer, Map<Integer, JSONObject>> styleMap;

  /** 有参构造 */
  public CellStyleSheetWriteHandler(Map<Integer, Map<Integer, JSONObject>> styleMap) {
    this.styleMap = styleMap;
  }

  /** 无参构造 */
  public CellStyleSheetWriteHandler() {}

  /** 在创建单元格之前调用 */
  @Override
  public void beforeCellCreate(
      WriteSheetHolder writeSheetHolder,
      WriteTableHolder writeTableHolder,
      Row row,
      Head head,
      Integer columnIndex,
      Integer relativeRowIndex,
      Boolean isHead) {}

  /** 在单元格创建后调用 */
  @Override
  public void afterCellCreate(
      WriteSheetHolder writeSheetHolder,
      WriteTableHolder writeTableHolder,
      Cell cell,
      Head head,
      Integer relativeRowIndex,
      Boolean isHead) {}

  /** 在单元上的所有操作完成后调用 */
  @Override
  public void afterCellDispose(CellWriteHandlerContext context) {
    WriteCellData<?> cellData = context.getFirstCellData();
    Cell cell = context.getCell();
    WriteSheetHolder writeSheetHolder = context.getWriteSheetHolder();
    // 当head需要单独设置样式时,这里通过isHead判断区分
    Head head = context.getHeadData();
    Boolean isHead = context.getHead();
    // 获取当前样式/数据,设置后立即生效

    // 当前行的第i列
    int i = cell.getColumnIndex();
    // 第j行
    int j = cell.getRowIndex();
    if (styleMap.containsKey(j)) {
      Map<Integer, JSONObject> cStyleMap = styleMap.get(j);
      if (cStyleMap.containsKey(i)) {
        JSONObject styles = cStyleMap.get(i);
        if (styles != null && styles.keySet().size() > 0) {
          // 根据单元格获取workbook
          Workbook workbook = cell.getSheet().getWorkbook();
          // 单元格策略,获取(没有则新建)当前样式/数据,设置后立即生效
          WriteCellStyle contentWriteCellStyle = cellData.getOrCreateStyle();
          // 这种直接创建的方式不会直接生效
          //          WriteCellStyle contentWriteCellStyle = new WriteCellStyle();

          // 设置单元格上下左右边框
          if (styles.containsKey("bottomBorderSize")) {
            contentWriteCellStyle.setBorderBottom(
                BorderStyle.valueOf(styles.getShort("bottomBorderSize")));
          }
          if (styles.containsKey("leftBorderSize")) {
            contentWriteCellStyle.setBorderLeft(
                BorderStyle.valueOf(styles.getShort("leftBorderSize")));
          }
          if (styles.containsKey("rightBorderSize")) {
            contentWriteCellStyle.setBorderRight(
                BorderStyle.valueOf(styles.getShort("rightBorderSize")));
          }
          if (styles.containsKey("topBorderSize")) {
            contentWriteCellStyle.setBorderTop(
                BorderStyle.valueOf(styles.getShort("topBorderSize")));
          }
          // 边框颜色
          if (styles.containsKey("bottomBorderColor")) {
            contentWriteCellStyle.setBottomBorderColor(styles.getShort("bottomBorderColor"));
          }
          if (styles.containsKey("leftBorderColor")) {
            contentWriteCellStyle.setLeftBorderColor(styles.getShort("leftBorderColor"));
          }
          if (styles.containsKey("rightBorderColor")) {
            contentWriteCellStyle.setRightBorderColor(styles.getShort("rightBorderColor"));
          }
          if (styles.containsKey("topBorderColor")) {
            contentWriteCellStyle.setTopBorderColor(styles.getShort("topBorderColor"));
          }

          // 设置背景颜色
          if (styles.containsKey("bgColor")) {
            // 设置默认填充模式为单色填充,否则颜色设置不生效;若设置了此项但不设置前景颜色,单元格会显示黑色,所以fillPattern务必与fillForeground同时设置
            contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
            // 设置了前景色后网格线默认会隐藏
            contentWriteCellStyle.setFillForegroundColor(styles.getShort("bgColor"));
          }
          // 创建字体实例
          WriteFont cellWriteFont = new WriteFont();
          // 设置字体
          if (styles.containsKey("fontName")) {
            cellWriteFont.setFontName(styles.getString("fontName"));
          }
          // 设置字体大小
          if (styles.containsKey("fontSize")) {
            cellWriteFont.setFontHeightInPoints(styles.getShort("fontSize"));
          }
          // 设置字体粗细
          if (styles.containsKey("fontBold")) {
            cellWriteFont.setBold(styles.getBoolean("fontBold"));
          }
          // 设置字体颜色
          if (styles.containsKey("fontColor")) {
            cellWriteFont.setColor(styles.getShort("fontColor"));
          }
          contentWriteCellStyle.setWriteFont(cellWriteFont);
        }
      }
    }
  }
}

设置宽度自适应(粗略)

注意这个方法一定要在多个sheet页时调用时设置sheetNo,否则只有第一个sheet页生效

public class CellWidthStyleStrategy extends AbstractColumnWidthStyleStrategy {

  /** 单元格的最大宽度 */
  private static final int MAX_COLUMN_WIDTH = 50;
  /** 缓存(第一个Map的键是sheet的index, 第二个Map的键是列的index, 值是数据长度) */
  private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap(8);

  /** 重写设置列宽的方法 */
  @Override
  protected void setColumnWidth(
      WriteSheetHolder writeSheetHolder,
      List<WriteCellData<?>> cellDataList,
      Cell cell,
      Head head,
      Integer relativeRowIndex,
      Boolean isHead) {
    boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
    // 当时表头或者单元格数据列表有数据时才进行处理
    if (needSetWidth) {
      Map<Integer, Integer> maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo());

      if (maxColumnWidthMap == null) {
        maxColumnWidthMap = new HashMap(16);
        CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
      }
      // 获取数据长度
      Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
      if (columnWidth >= 0) {
        if (columnWidth > MAX_COLUMN_WIDTH) {
          columnWidth = MAX_COLUMN_WIDTH;
        }
        // 确保一个列的列宽以表头为主,如果表头已经设置了列宽,单元格将会跟随表头的列宽
        Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());

        if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
          maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
          // 如果使用EasyExcel默认表头,那么使用columnWidth * 512
          // 如果不使用EasyExcel默认表头,那么使用columnWidth * 256
          // 如果是自己定义的字体大小,可以再去测试这个参数常量
          writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
        }
      }
    }
  }

  /**
   * 获取当前单元格的数据长度
   *
   * @param cellDataList
   * @param cell
   * @param isHead
   * @return
   */
  private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {
    if (isHead) {
      return cell.getStringCellValue().getBytes().length;
    } else {
      WriteCellData cellData = cellDataList.get(0);
      CellDataTypeEnum type = cellData.getType();
      if (type == null) {
        return -1;
      } else {
        switch (type) {
          case STRING:
            return cellData.getStringValue().getBytes().length;
          case BOOLEAN:
            return cellData.getBooleanValue().toString().getBytes().length;
          case NUMBER:
            return cellData.getNumberValue().toString().getBytes().length;
          default:
            return -1;
        }
      }
    }
  }
}
设置根据文本换行符自适应行高
public class CellHeightStyleStrategy extends AbstractRowHeightStyleStrategy {
  /** 默认行高, 15pt=300twips(setHeight的单位) */
  private static final Integer DEFAULT_HEIGHT = 300;

  @Override
  protected void setHeadColumnHeight(Row row, int relativeRowIndex) {}

  @Override
  protected void setContentColumnHeight(Row row, int relativeRowIndex) {
    Iterator<Cell> cellIterator = row.cellIterator();
    if (!cellIterator.hasNext()) {
      return;
    }

    // 默认为 1行高度
    Integer maxHeight = 1;
    while (cellIterator.hasNext()) {
      Cell cell = cellIterator.next();
      switch (cell.getCellType()) {
        case STRING:
          if (cell.getStringCellValue().indexOf("\n") != -1) {
            int length = cell.getStringCellValue().split("\n").length;
            maxHeight = Math.max(maxHeight, length);
          }
          break;
        default:
          break;
      }
    }
    row.setHeight((short) (maxHeight * DEFAULT_HEIGHT));
  }
}
多sheet页导出,多sheet页独立样式设置示例
/**
   * @description 支持自定义单元格样式、多sheet页导出
   * @param response 文件导出响应
   * @param fileName 导出文件名
   * @param list 导出文件表头数据
   * @param multiStyleMap 包含每个sheet单元格样式数据,第一个map的key为sheet页名称,第二个map的key为第i行,第三个map的key为第j列
   * @param multiSheetMap 包含sheet名称和sheet数据,第一个map的key为sheet页名称
   * @throws
   * @return
   */
  public void exportFile(
      HttpServletResponse response,
      String fileName,
      List<List<String>> list,
      Map<String, Map<Integer, Map<Integer, JSONObject>>> multiStyleMap,
      Map<String, List<List<Object>>> multiSheetMap) {
    if (Optional.ofNullable(multiSheetMap).isEmpty() || multiSheetMap.keySet().size() < 1) {
      return;
    }
    try {
      response.setContentType("multipart/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
      response.setCharacterEncoding("utf-8");
      String exportFileName = URLEncoder.encode(fileName, "UTF-8");
      response.setHeader("Content-disposition", "attachment;filename=" + exportFileName + ".xlsx");
	  // 这里使用ExcelWriter方便后续单独添加sheet页数据
      ExcelWriter excelWriter =
          EasyExcel.write(response.getOutputStream())
              .head(list)
              .useDefaultStyle(true)
              .build();
     //使用sheet名称作为key,value保存每个sheet页的数据
      multiSheetMap
          .keySet()
          .forEach(
              key -> {
              //获取每个sheet的独立样式数据
                Map<Integer, Map<Integer, JSONObject>> styleMap = multiStyleMap.get(key);
                CellStyleSheetWriteHandler cellStyleSheetWriteHandler =
                    new CellStyleSheetWriteHandler(styleMap);
                List<List<Object>> sheetData = multiSheetMap.get(key);
                WriteSheet sheet =
                    EasyExcel.writerSheet(key)
                        .registerWriteHandler(cellStyleSheetWriteHandler)
                        .build();
                excelWriter.write(sheetData, sheet);
              });
      //关闭流
      excelWriter.finish();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

单元格设置下拉值显示

package xxx;

import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;

import java.util.HashMap;
import java.util.Map;

/**
 * @Descpription 自定义sheet下拉
 */
public class CustomSheetWriteHandler implements SheetWriteHandler {
  Map<Integer, String[]> mapDropDown = new HashMap<>();

  public CustomSheetWriteHandler(Map<Integer, String[]> mapDropDown) {
    this.mapDropDown = mapDropDown;
  }

  public CustomSheetWriteHandler() {}

  @Override
  public void beforeSheetCreate(
      WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {}

  @Override
  public void afterSheetCreate(
      WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
    Workbook workbook = writeWorkbookHolder.getWorkbook();
    Sheet sheet = writeSheetHolder.getSheet();
    DataValidationHelper helper = sheet.getDataValidationHelper();
    for (Map.Entry<Integer, String[]> entry : mapDropDown.entrySet()) {
     /***设置隐藏sheet处理下拉值过多不显示问题**/
      String sheetName = "hidden" + entry.getKey();
      Sheet providerSheet = workbook.createSheet(sheetName);
      Name categoryName = workbook.createName();
      categoryName.setNameName(sheetName);
      /***起始行、终止行、起始列、终止列**/
      CellRangeAddressList addressList =
          new CellRangeAddressList(1, 65535, entry.getKey(), entry.getKey());
      String[] values = entry.getValue();
      for (int i = 0, len = values.length; i < len; i++) {
        Row row = providerSheet.getRow(i);
        if (row == null) {
          row = providerSheet.createRow(i);
        }
        row.createCell(entry.getKey()).setCellValue(values[i]);
      }
      String excelLine = getExcelLine(entry.getKey());
      String refers =
          "=" + sheetName + "!$" + excelLine + "$1:$" + excelLine + "$" + (values.length);
      /***设置下拉框数据**/
      DataValidationConstraint constraint = helper.createFormulaListConstraint(refers);
      DataValidation dataValidation = helper.createValidation(constraint, addressList);
      sheet.addValidationData(dataValidation);
      /** 设置列为隐藏 */
      int hiddenIndex = workbook.getSheetIndex(sheetName);
      if (!workbook.isSheetHidden(hiddenIndex)) {
        workbook.setSheetHidden(hiddenIndex, true);
      }
      /***处理Excel兼容性问题**/
      if (dataValidation instanceof XSSFDataValidation) {
        dataValidation.setSuppressDropDownArrow(true);
        dataValidation.setShowErrorBox(true);
      } else {
        dataValidation.setSuppressDropDownArrow(false);
      }
    }
  }

  /**
   * @param num 列数
   * @return java.lang.String @Description 返回excel列标A-Z-AA-ZZ
   */
  public static String getExcelLine(int num) {
    String line = "";
    int first = num / 26;
    int second = num % 26;
    if (first > 0) {
      line = (char) ('A' + first - 1) + "";
    }
    line += (char) ('A' + second) + "";
    return line;
  }
}

单元格导出多图片(自定义单元格行、高)

package xxx;

import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @description 自定义单个单元格多图片导出
 */
public class CustomImageCellWriteHandler implements CellWriteHandler {
  private List<String> repeats = new ArrayList<>();
  //图片需要放置的列号
  private List<Integer> imageColumnIndexs = new ArrayList<>();

  public CustomImageCellWriteHandler(
      List<Integer> imageColumnIndexs) {
    this.imageColumnIndexs = imageColumnIndexs;
  }

  public CustomImageCellWriteHandler() {}

  @Override
  public void beforeCellCreate(
      WriteSheetHolder writeSheetHolder,
      WriteTableHolder writeTableHolder,
      Row row,
      Head head,
      Integer columnIndex,
      Integer relativeRowIndex,
      Boolean isHead) {}

  @Override
  public void afterCellCreate(
      WriteSheetHolder writeSheetHolder,
      WriteTableHolder writeTableHolder,
      Cell cell,
      Head head,
      Integer relativeRowIndex,
      Boolean isHead) {}

  @Override
  public void afterCellDataConverted(
      WriteSheetHolder writeSheetHolder,
      WriteTableHolder writeTableHolder,
      WriteCellData<?> cellData,
      Cell cell,
      Head head,
      Integer relativeRowIndex,
      Boolean isHead) {
    if (isHead) {
      return;
    }
    // 将要插入图片的单元格的type设置为空,下面再填充图片
    if (imageColumnIndexs.contains(cell.getColumnIndex())) {
      cellData.setType(CellDataTypeEnum.EMPTY);
    }
  }

  @Override
  public void afterCellDispose(
      WriteSheetHolder writeSheetHolder,
      WriteTableHolder writeTableHolder,
      List<WriteCellData<?>> cellDataList,
      Cell cell,
      Head head,
      Integer relativeRowIndex,
      Boolean isHead) {
    Sheet sheet = cell.getSheet();

    if (isHead || cellDataList == null) {
      return;
    }
    if (!imageColumnIndexs.contains(cell.getColumnIndex())) {
      return;
    }
    String key = cell.getRowIndex() + "_" + cell.getColumnIndex();
    if (repeats.contains(key)) {
      return;
    }
    CellData cellData = cellDataList.get(0);
    String fieldids = cellData.getStringValue();
    if ("".equals(fieldids)) {
      return;
    }
    String[] fieldIdArr = fieldids.split(",");
    // 默认图片 1*2.52(cm)
    // 图片列 13(宽字符) * 60 (高pt)
    sheet.getRow(cell.getRowIndex()).setHeight((short) 1133);
    sheet.setColumnWidth(cell.getColumnIndex(), 256 * 13);
    for (int i = 0; i < fieldIdArr.length; i++) {
      try {
        this.insertImage(sheet, cell, fieldIdArr[i], i);
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }
  }

  private void insertImage(Sheet sheet, Cell cell, String fieldid, int i) throws IOException {
    int index = sheet.getWorkbook().addPicture(getImage(fieldid), XSSFWorkbook.PICTURE_TYPE_PNG);
    Drawing drawing = sheet.getDrawingPatriarch();
    if (drawing == null) {
      drawing = sheet.createDrawingPatriarch();
    }
    CreationHelper helper = sheet.getWorkbook().getCreationHelper();
    ClientAnchor anchor = helper.createClientAnchor();

    // 设置图片位置,图片放置在一个单元格,结束行列无需新增
    // 开始列数
    anchor.setCol1(cell.getColumnIndex());
    // 开始行
    anchor.setRow1(cell.getRowIndex());
    // 结束列数
    anchor.setCol2(cell.getColumnIndex());
    // 结束行
    anchor.setRow2(cell.getRowIndex());
    // 设置图片可以随着单元格移动
    anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_DONT_RESIZE);
    // 图片宽高 2.52*1(cm)
    int picWeight = 907200;
    int picHeight = 360000;
    // 按列排放
    // 左上边界相对左上角偏移量(单位EMU)
    anchor.setDx1(0);
    anchor.setDy1(picHeight * i);
    // 右下边界相对左上角偏移量
    anchor.setDx2(picWeight);
    anchor.setDy2(picHeight * i + picHeight);
    // 插入图片
    Picture pict = drawing.createPicture(anchor, index);
  }

  private byte[] getImage(String fieldid) {
    byte[] bytes = null;
    try {
    //......
      //需要根据业务场景,根据图片路径或图片流保存成byte[]
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return bytes;
  }
}

附:Excel、POI单元格宽高转化

excel行高单位

1、行高:以磅(pt)为单位,默认一行高度15pt=300twips
2、列宽:一个列宽单位等于“常规”样式中一个字符的宽度。对于比例字体,则使用字符“0”(零)的宽度。

POI设置单元格宽高

setColumnWidth(colindex, width):设置第(colindex+1)列宽度为width个字符,一个字符宽度为256
setHeight 高度单位为twips
1英寸=72磅(pt)=25.4毫米=1440缇
1px = 0.75pt
1px = 15twips
1pt = 20twips

ClientAnchor设置dx,dy偏移量 单位为EMU

1cm = 360000EMUs、1px = 9525EMUs

参考链接:
单元格放置多图片按行排放方案

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
生成 Java 自定义 Excel 表格导出代码可以使用以下代码片段作为参考: ```java public class ExcelUtil { /** * 导出 Excel 表格 * * @param filename 文件名 * @param headers 表头 * @param data 表格数据 * @param out 输出流 * @throws IOException */ public static void exportExcel(String filename, String[] headers, List<Map<String, Object>> data, OutputStream out) throws IOException { XSSFWorkbook workbook = new XSSFWorkbook(); XSSFSheet sheet = workbook.createSheet(filename); // 设置表头样式 XSSFCellStyle headerStyle = workbook.createCellStyle(); headerStyle.setAlignment(HorizontalAlignment.CENTER); headerStyle.setFillForegroundColor(IndexedColors.LIGHT_TURQUOISE.getIndex()); headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); headerStyle.setBorderTop(BorderStyle.THIN); headerStyle.setBorderBottom(BorderStyle.THIN); headerStyle.setBorderLeft(BorderStyle.THIN); headerStyle.setBorderRight(BorderStyle.THIN); // 创建表头行 XSSFRow headerRow = sheet.createRow(0); for (int i = 0; i < headers.length; i++) { XSSFCell cell = headerRow.createCell(i); cell.setCellValue(headers[i]); cell.setCellStyle(headerStyle); } // 设置表格数据样式 XSSFCellStyle dataStyle = workbook.createCellStyle(); dataStyle.setBorderTop(BorderStyle.THIN); dataStyle.setBorderBottom(BorderStyle.THIN); dataStyle.setBorderLeft(BorderStyle.THIN); dataStyle.setBorderRight(BorderStyle.THIN); // 填充表格数据 for (int i = 0; i < data.size(); i++) { XSSFRow dataRow = sheet.createRow(i + 1); Map<String, Object> rowData = data.get(i); for (int j = 0; j < headers.length; j++) { XSSFCell cell = dataRow.createCell(j); Object value = rowData.get(headers[j]); if (value != null) { if (value instanceof String) { cell.setCellValue((String) value); } else if (value instanceof Integer) { cell.setCellValue((Integer) value); } else if (value instanceof Double) { cell.setCellValue((Double) value); } else if (value instanceof Date) { cell.setCellValue((Date) value); XSSFCellStyle dateStyle = workbook.createCellStyle(); dateStyle.setDataFormat(workbook.getCreationHelper().createDataFormat().getFormat("yyyy-MM-dd HH:mm:ss")); cell.setCellStyle(dateStyle); } } cell.setCellStyle(dataStyle); } } // 自适应列宽 for (int i = 0; i < headers.length; i++) { sheet.autoSizeColumn(i); } // 输出 Excel 文件 workbook.write(out); workbook.close(); } } ``` 在这里,我们使用了 Apache POI 库来生成 Excel 表格。在 `exportExcel` 方法中,我们首先创建了一个 `XSSFWorkbook` 对象来表示 Excel 文件。然后,我们创建了一个 `XSSFSheet` 对象来表示表格,并设置了表头样式和表头行。接着,我们填充了表格数据,并根据数据类型设置了单元格样式。最后,我们自适应列宽,并将 Excel 文件输出到指定的输出流中。 要使用该工具类导出 Excel 表格,只需要调用 `exportExcel` 方法即可,如下所示: ```java List<Map<String, Object>> data = new ArrayList<>(); Map<String, Object> row1 = new LinkedHashMap<>(); row1.put("id", 1); row1.put("name", "张三"); row1.put("age", 20); row1.put("create_time", new Date()); data.add(row1); Map<String, Object> row2 = new LinkedHashMap<>(); row2.put("id", 2); row2.put("name", "李四"); row2.put("age", 22); row2.put("create_time", new Date()); data.add(row2); String[] headers = {"id", "name", "age", "create_time"}; String filename = "test.xlsx"; OutputStream out = new FileOutputStream(filename); ExcelUtil.exportExcel(filename, headers, data, out); out.close(); ``` 在这个例子中,我们首先创建了一个包含两行数据的表格数据 `data`,然后创建了一个包含表头信息的字符串数组 `headers`,并指定了导出Excel 文件名 `filename` 和输出流 `out`。最后,我们调用 `exportExcel` 方法导出 Excel 表格,并关闭输出流。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值