普通的单表以及数据都是单列呈现的,没有各种合并单元格的样式可以直接使用easyexcel,使用注解加实体类,java实体类属性一一对应excel每列,直接导出非常简单。不过最近的需求非常复杂,需要导出的工作簿,里边有几十张sheet,而且每个sheet的表格样式都是花里胡哨的,而且还要带各种本地超链接以及下拉选型数据有效性验证,以及单元格还得插入图片之类的,样式很复杂,所以使用easyexcel不太好处理,但是又不想直接使用原生的apache poi去逐个单元格去处理,那样的话,一个工作簿一个月都处理不完,公司肯定也是不想花钱用商业版的java工具包(如 Aspose.Cells for Java或者Spire.XLS for Java),所以只能暂时锁定了easypoi,可以灵活利用模版去设置数据填充,同时又可以很好保留表格的整体样式和布局。
做一个测试,设计了一个比较简单的模版,模版文件是这样的
暂时只设计了两个sheet。
模板表中的指令说明:
- 三目运算 {{test ? obj:obj2}}
- n: 表示 这个cell是数值类型 {{n:}}
- le: 代表长度{{le:()}} 在if/else 运用{{le:() > 8 ? obj1 : obj2}}
- fd: 格式化时间 {{fd:(obj;yyyy-MM-dd)}}
- fn: 格式化数字 {{fn:(obj;###.00)}}
- fe: 遍历数据,创建row
- !fe: 遍历数据不创建row
- $fe: 下移插入,把当前行,下面的行全部下移.size()行,然后插入
- #fe: 横向遍历
- v_fe: 横向遍历值
- !if: 删除当前列 {{!if:(test)}}
- 单引号表示常量值 ‘’ 比如’1’ 那么输出的就是 1
- &NULL& 空格
- &INDEX& 表示循环中的序号,自动添加
- ]] 换行符 多行遍历导出
- sum: 统计数据
- cal: 基础的+-X% 计算
- dict: 字典
- i18n: 国际化
我用到了时间格式的设置的指令和金额格式指令(设置千位分隔符格式)以及列表数据的循环遍历
$fe: 冒号后是列表数据集合的名字,然后跟每行数据的属性名,默认用t表示每行数据对象,也可以不要t
然后项目案例代码
主要的Maven依赖包
<!-- https://mvnrepository.com/artifact/cn.afterturn/easypoi-spring-boot-starter -->
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
然后模拟测试方法
import cn.afterturn.easypoi.entity.ImageEntity;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.hutool.core.img.ImgUtil;
import lombok.Data;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.ResourceUtils;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
/**
* @author xiaomifeng1010
* @version 1.0
* @date: 2024-10-26 13:23
* @Description
*/
public class ExportManySheetTest {
public static void main(String[] args) throws IOException {
// 获取要导出的模版文件
TemplateExportParams params = new TemplateExportParams(
"excel/exportManySheet.xlsx", 0, 1);
// params.setHeadingRows(2);
// params.setHeadingStartRow(2);
// 每个sheet的数据对应一个map
Map<Integer, Map<String, Object>> sheetMap = new HashMap<>();
// 处理第一个sheet(项目说明)
Map<String, Object> sheet1Map = new HashMap<String, Object>();
// 模拟从数据库中查询出项目信息
ProjectAnalysis project = new ProjectAnalysis();
project.setProjectName("物流项目");
project.setAnalysisItem("物流项目分析可行性的时候要关注多种因素");
project.setTechnicalSkill("java,python,mysql");
project.setAmount(BigDecimal.valueOf(100000d));
project.setContent("前景广阔");
project.setMakeDate(new Date());
project.setManufacturingCost("10000000元");
project.setDay(120);
project.setOtherItem("补充事项");
project.setReportPerson("xiaomifeng1010");
sheet1Map.put("projectName", project.getProjectName());
sheet1Map.put("analysisItem", project.getAnalysisItem());
sheet1Map.put("technicalSkill", project.getTechnicalSkill());
sheet1Map.put("amount", project.getAmount());
sheet1Map.put("content", project.getContent());
sheet1Map.put("makeDate", project.getMakeDate());
sheet1Map.put("manufacturingCost", project.getManufacturingCost());
sheet1Map.put("day", project.getDay());
sheet1Map.put("otherItem", project.getOtherItem());
sheet1Map.put("reportPerson", project.getReportPerson());
// 将第一个sheet的数据放入map中
sheetMap.put(0, sheet1Map);
// 开始处理工作簿中的第二个sheet(接口内容)
Map<String, Object> sheet2Map = new HashMap<>();
// 模拟从数据库中查出来4条数据
List<InterfaceInfo> interfaceList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
InterfaceInfo interfaceInfo = new InterfaceInfo();
interfaceInfo.setInterfaceName("接口" + i);
// 不用设置,可以自动填充(&INDEX&自动填充索引数据)
// interfaceInfo.setInterfaceNum("编号" + i);
interfaceInfo.setInterfaceChargeMan("负责人" + i);
interfaceInfo.setTestPerson("测试人员" + i);
interfaceInfo.setTestType("测试类型" + i);
ImageEntity image = new ImageEntity();
image.setHeight(200);
image.setWidth(500);
image.setType("data");
// image.setRowspan(4);
// image.setColspan(2);
if (i < 2) {
// image.setUrl("imgs/company/baidu.png");
// 直接读取图片为字节数组
ClassPathResource classPathResource = new ClassPathResource("imgs"+ File.separator +"company"+ File.separator +"baidu.png");
InputStream inputStream = classPathResource.getInputStream();
// BufferedImage bufferedImage = ImgUtil.read(inputStream);
// ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// ImageIO.write(bufferedImage, "png",byteArrayOutputStream );
// image.setData(byteArrayOutputStream.toByteArray());
byte[] bytes = IOUtils.toByteArray(inputStream);
System.out.println("bytes:" + bytes.toString());
image.setData(bytes);
} else {
// image.setUrl("imgs/company/ali.png");
// 直接读取图片为字节数组
ClassPathResource classPathResource = new ClassPathResource("imgs"+ File.separator +"company"+ File.separator +"ali.png");
InputStream inputStream = classPathResource.getInputStream();
// BufferedImage bufferedImage = ImgUtil.read(inputStream);
// ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// ImageIO.write(bufferedImage, "png",byteArrayOutputStream );
// image.setData(byteArrayOutputStream.toByteArray());
image.setData(IOUtils.toByteArray(inputStream));
}
interfaceInfo.setImg(image);
interfaceList.add(interfaceInfo);
}
// 填充到map中
List<Map<String, Object>> sheetDataList = new ArrayList<>();
for (InterfaceInfo interfaceInfo : interfaceList) {
Map<String, Object> temp = new HashMap<>();
temp.put("interfaceName", interfaceInfo.getInterfaceName());
temp.put("interfaceNum", interfaceInfo.getInterfaceNum());
temp.put("interfaceChargeMan", interfaceInfo.getInterfaceChargeMan());
temp.put("testPerson", interfaceInfo.getTestPerson());
temp.put("testType", interfaceInfo.getTestType());
temp.put("img", interfaceInfo.getImg());
sheetDataList.add(temp);
}
// 表中的$fe:后的集合的名字是list,就是map绑定的key
sheet2Map.put("list", sheetDataList);
// 将第二个sheet中的数据放到map中
sheetMap.put(1, sheet2Map);
// 导出到本地
Workbook book = ExcelExportUtil.exportExcel(sheetMap, params);
// 获取resources路径
String path = ExportManySheetTest.class.getClassLoader().getResource("").getPath();
System.out.println("resources的目录:" + path);
try {
FileOutputStream fos = new FileOutputStream(path + "test.xls");
book.write(fos);
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Data
class ProjectAnalysis {
private String projectName;
private String projectNum;
// 分析事项
private String analysisItem;
private String technicalSkill;
// 生产能力
private String produceAbility;
// 制造费用
private String manufacturingCost;
// 开发时间
private Integer day;
// 新增费用
private BigDecimal amount;
// 其他事项
private String otherItem;
// 报告人
private String reportPerson;
// 编制时间
private Date makeDate;
// 会签内容
private String content;
}
@Data
class InterfaceInfo {
private String interfaceName;
private String interfaceNum;
// 接口负责人
private String interfaceChargeMan;
// 测试人员
private String testPerson;
private String testType;
// 图片对象
private ImageEntity img;
}
注意这个框架的坑还是挺多的,官方文档说的是xls和xlsx两种格式都支持的,但是实测,如果你的模版文件是xls,在读取的时候会报错的
比如我把这行代码改一下:
将resources目录下的模版文件后缀改成xls,不只是直接改后缀,因为我有两个同名的模版文件,后缀不同,相当于是让程序读另外一个xls后缀的模版文件
当我改成xls那个文件时,运行方法就会报错:
就会提示XSSF对象不能转换成HSSF对象
HSSFWorkbook:是操作Excel2003以前(包括2003)的版本,扩展名是.xls
XSSFWorkbook:是操作Excel2007的版本,扩展名是.xlsx
但是如果是xlsx后缀的文件,就可以正常读取
然后运行后,不会报错
输出的时候,后缀格式目前两种都支持,没出现错误
可以查看到导出的test文件
可以看到两个sheet表中都填充好了数据;但是图片目前导出也是也有bug,我尝试使用了图片的实体类设置url方式获取图片数据,以及data方式直接获取图片字节数组数据,去填充图片列,目前导出图片都是空白:
网上有很多其他人也有这个问题,导出图片空白,应该是新版本的bug问题,目前还没有修复,
去年就有人遇到了,不过似乎一直没解决这个问题。
如果要图片导出,可能还是需要自己手动使用apache poi原生api自己实现一下,不过如果你的需求中,不需要图片的导出,那这个框架已经够用了!
标签:sheet,String,import,导出,private,project,new,put,easypoi From: https://blog.csdn.net/u011174699/article/details/143257608