首页IT科技excel表格导出文字(EasyExcel复杂表头导出(一对多)升级版)

excel表格导出文字(EasyExcel复杂表头导出(一对多)升级版)

时间2025-05-06 10:05:12分类IT科技浏览3689
导读:一、前言 在之前写的 EasyExcel复杂表头导出(一对多)的博客的结尾,受限于当时的能力和精力,留下一些问题及展望。现在写下此博客,目的就是解决之前遗留的问题。...

一            、前言

        在之前写的 EasyExcel复杂表头导出(一对多)的博客的结尾            ,受限于当时的能力和精力                  ,留下一些问题及展望            。现在写下此博客      ,目的就是解决之前遗留的问题                  。

        背景介绍         ,见上述链接指向的博客                  ,这里主要通过自定义拦截器的形式来完美解决      。

二                  、导出功能的实现

2.1 Entity 对象

import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ContentRowHeight; import com.alibaba.excel.annotation.write.style.HeadRowHeight; import com.alibaba.excel.annotation.write.style.HeadStyle; import com.alibaba.excel.converters.string.StringImageConverter; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import java.net.URL; @Data @EqualsAndHashCode @HeadRowHeight(30) @ContentRowHeight(80) @ColumnWidth(15) @HeadStyle(fillForegroundColor = 44) @NoArgsConstructor @AllArgsConstructor class Customer { @ExcelProperty({"客户编号"}) private String userCode; @ExcelProperty({"客户名称"}) private String userName; @ColumnWidth(25) @ExcelProperty({"客户所在地址"}) private String address; @ExcelProperty({"联系人信息", "联系人姓名"}) private String personName; @ExcelProperty({"联系人信息", "联系电话"}) private String telephone; @ExcelProperty({"图片"}) private URL picture; /** * 你也可以通过字符串的形式来保存图片         ,具体说明见注意事项3.1 */ //@ExcelProperty(converter = StringImageConverter.class, value = {"本地图片"}) //private String localPic; }

2.2 Controller 层

@PostMapping("/exportExcel") @ApiOperation("导出Excel") public void exportExcel(HttpServletResponse response) throws Exception { // 查询需要导出的数据 List result = getData(); // 1设置表头样式 WriteCellStyle headStyle = new WriteCellStyle(); // 1.1设置表头数据居中 headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); // 2设置表格内容样式 WriteCellStyle bodyStyle = new WriteCellStyle(); // 2.1设置表格内容水平居中 bodyStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); // 2.2设置表格内容垂直居中 bodyStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 3设置表格sheet样式 WriteSheet sheet = EasyExcel.writerSheet("客户信息").head(Customer.class).sheetNo(1).build(); // 4拿到表格处理对象 ExcelWriter writer = EasyExcel.write(response.getOutputStream()).needHead(true).excelType(ExcelTypeEnum.XLSX) // 设置需要待合并的行和列         。参数1:数值数组      ,指定需要合并的列;参数2:数值                  ,指定从第几行开始合并 .registerWriteHandler(new ExcelMergeCellHandler(new int[]{0, 1, 2, 5}, 0)) // 设置单元格的风格样式 .registerWriteHandler(new HorizontalCellStyleStrategy(headStyle, bodyStyle)) .build(); // 5写入excel数据 writer.write(result, sheet); // 6通知浏览器以附件的形式下载处理            ,设置返回头要注意文件名有中文 response.setHeader("Content-disposition", "attachment;filename=" + new String("客户信息表".getBytes("gb2312"), "ISO8859-1") + ".xlsx"); response.setContentType("multipart/form-data"); response.setCharacterEncoding("utf-8"); writer.finish(); }

2.3 自定义拦截器(ExcelMergeCellHandler)

import com.alibaba.excel.metadata.Head; 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 lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; import java.util.List; /** * @author DaHuaJia * @Description 自定义单元格合并处理Handler类 * @Date 2022-08-18 19:25:58 */ @Data @NoArgsConstructor @AllArgsConstructor public class ExcelMergeCellHandler implements CellWriteHandler { // 需要合并的列   ,从0开始算 private int[] mergeColIndex; // 从指定的行开始合并                  ,从0开始算 private int mergeRowIndex; /** * 在单元格上的所有操作完成后调用               ,遍历每一个单元格,判断是否需要向上合并 */ @Override public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { // 获取当前单元格行下标 int currRowIndex = cell.getRowIndex(); // 获取当前单元格列下标 int currColIndex = cell.getColumnIndex(); // 判断是否大于指定行下标               ,如果大于则判断列是否也在指定的需要的合并单元列集合中 if (currRowIndex > mergeRowIndex) { for (int i = 0; i < mergeColIndex.length; i++) { if (currColIndex == mergeColIndex[i]) { /** * 获取列表数据的唯一标识                  。不同集合的数据即使数值相同也不合并 * 注意:我这里的唯一标识为客户编号(Customer.userCode)                  ,在第一列   ,即下标为0         。大家需要结合业务逻辑来做修改 */ // 获取当前单元格所在的行数据的唯一标识 Object currCode = cell.getRow().getCell(0).getStringCellValue(); // 获取当前单元格的正上方的单元格所在的行数据的唯一标识 Object preCode = cell.getSheet().getRow(currRowIndex - 1).getCell(0).getStringCellValue(); // 判断两条数据的是否是同一集合            ,只有同一集合的数据才能合并单元格 if(preCode.equals(currCode)){ // 如果都符合条件                  ,则向上合并单元格 mergeWithPrevRow(writeSheetHolder, cell, currRowIndex, currColIndex); break; } } } } } /** * 当前单元格向上合并 * * @param writeSheetHolder 表格处理句柄 * @param cell 当前单元格 * @param currRowIndex 当前行 * @param currColIndex 当前列 */ private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int currRowIndex, int currColIndex) { // 获取当前单元格数值 Object currData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue(); // 获取当前单元格正上方的单元格对象 Cell preCell = cell.getSheet().getRow(currRowIndex - 1).getCell(currColIndex); // 获取当前单元格正上方的单元格的数值 Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue(); // 将当前单元格数值与其正上方单元格的数值比较 if (preData.equals(currData)) { Sheet sheet = writeSheetHolder.getSheet(); List<CellRangeAddress> mergeRegions = sheet.getMergedRegions(); // 当前单元格的正上方单元格是否是已合并单元格 boolean isMerged = false; for (int i = 0; i < mergeRegions.size() && !isMerged; i++) { CellRangeAddress address = mergeRegions.get(i); // 若上一个单元格已经被合并      ,则先移出原有的合并单元         ,再重新添加合并单元 if (address.isInRange(currRowIndex - 1, currColIndex)) { sheet.removeMergedRegion(i); address.setLastRow(currRowIndex); sheet.addMergedRegion(address); isMerged = true; } } // 若上一个单元格未被合并                  ,则新增合并单元 if (!isMerged) { CellRangeAddress cellRangeAddress = new CellRangeAddress(currRowIndex - 1, currRowIndex, currColIndex, currColIndex); sheet.addMergedRegion(cellRangeAddress); } } } }

2.4 getDate方法(用于模拟service层拿到的数据)

public static List<Customer> getData() throws Exception { List<Customer> data = new ArrayList<>(); Customer customer = new Customer("JiangXi", "江西电信公司", "江西省南昌市东湖区", "张三", "12345678910", new URL("https://www.yuucn.com/wp-content/uploads/2023/05/1684184128-4e6652a71714f6e.jpg")); data.add(customer); Customer customer2 = new Customer("JiangXi", "江西电信公司", "江西省南昌市东湖区", "李四", "15848563521", new URL("https://www.yuucn.com/wp-content/uploads/2023/05/1684184128-4e6652a71714f6e.jpg")); data.add(customer2); Customer customer3 = new Customer("GuangDong", "广东电信公司", "广东省广州市花都区", "小明", "15847953624", new URL("https://www.yuucn.com/wp-content/uploads/2023/05/1684184134-4e6652a71714f6e.jpg")); data.add(customer3); Customer customer4 = new Customer("GuangDong", "广东电信公司", "广东省广州市天河区", "小红", "16849531548", new URL("https://www.yuucn.com/wp-content/uploads/2023/05/1684184140-4e6652a71714f6e.jpg")); data.add(customer4); Customer customer5 = new Customer("GuangDong", "广东电信公司", "广东省广州市天河区", "小华", "16985632481", new URL("https://www.yuucn.com/wp-content/uploads/2023/05/1684184140-4e6652a71714f6e.jpg")); data.add(customer5); Customer customer6 = new Customer("BeiJing", "北京电信公司", "北京市东城区", "姜维", "16598645874", new URL("https://www.yuucn.com/wp-content/uploads/2023/05/1684184147-4e6652a71714f6e.jpg")); data.add(customer6); return data; }

2.5 效果

 三      、注意事项

3.1 图片导出问题

对于图片的导出         ,其字段可以有多种数据类型      ,官网就介绍了5种(File         、InputStream                  、String         、byte[]      、URL                  、WriteCellData<Void>)      。这里简要介绍一下String 和 URL                  。

1            、String 类型

/** *  如果图片地址通过String类型保存                  ,则需要加一个自带的类型转换器(StringImageConverter) */ @ExcelProperty(converter = StringImageConverter.class, value = {"本地图片"}) private String localPic;

2   、URL类型 

@ExcelProperty({"网络图片"}) private URL picture;

        经过测试发现            ,String类型只能保存本地图片地址   ,如果保存网络图片地址                  ,则会导致图片无法下载            。原因则是EasyExcel会把“//            ” 转换成 “\                  ”               ,导致地址错误   。

        因此,可以约定String类型用于保存本地图片地址               ,URL类型用于保存网络图片地址                  。

3.2 图片单元格合并问题

        图片类型单元格无法做到相同的图片合并单元格                  ,主要是因为无法通过单元格对象拿到图片的序列化值               。

3.3 表格样式

        表格的样式既可以表格样式类(例如:WriteCellStyle)来设置   ,也可以通过注解(例如:@HeadStyle)来设置            ,两者互补                  ,不冲突。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
niwibe/djangoredis