这几天客户提出了新的需求,要求记录要能够导出word,并且里面包含的图片也要导出来,这里借用EasyPOI来进行操作。
参考文章:https://blog.csdn.net/qq_34752942/article/details/112203419
1、引入相关Jar包
引入EasyPOI必要的Jar包,这里就不再多说,不知道引哪些的话请自行百度。
注意:建议使用EasyPOI 4.3.0+ 版本的,因为word图片循环是4.3.0以后的版本才支持的。切记!!!
2、后台代码
2.1、导出对象类
这里简单改造一个项目中使用的对象。关于照片的两个字段说明一下,这里是项目中图片上传到服务器之后,把图片在服务器的存储路径保存到数据库中,如果上传了多张图片,那么把就用逗号把路径拼接成一个字符串放入数据库。
public class ProblemRectify {
/**
* 标段名
*/
private String bidName;
/**
* 问题描述
*/
private String description;
/**
* 问题照片,路径字符串,以逗号分隔
*/
private String pictures;
/**
* 问题发现时间
*/
private LocalDate checkDate;
/**
* 问题整改时限
*/
private LocalDate rectifyDate;
/**
* 整改方案
*/
private String rectifyPlan;
/**
* 整改结果
*/
private String rectifyResult;
/**
* 整改后照片,路径字符串,以逗号分隔
*/
private String rectifyPictures;
/**
* 整改完成时间
*/
private LocalDate rectifyCompleteDate;
/**
* 整改状态
*/
private String rectifyStatus;
/**
* 问题责任人名字
*/
private String liablePersonName;
/**
* 问题发现人名字
*/
private String findPersonName;
/**
* 问题类型
*/
private String typeName;
/**
* 问题来源
*/
private String sourceName;
/**
* 问题分类代码
*/
private String sortName;
/**
* 问题级别代码
*/
private String levelName;
/**
* 责任单位
*/
private String proOrganizationName;
/**
* 所在桩号
*/
private String pileNum;
}
2.2、Word模板
要用EasyPOI的word模板导出,那么就要事先定义好word模板,并放入项目的指定位置。这里建议在resources下新建一个目录,我这里建的目录名是exportTemplate,大家可以根据实际需要自行创建。
Word模板如下图所示:
注意:关于EasyPOI模板指令的介绍,请参考EasyPOI的官网。仅说一下我在写word模板时遇到的问题:写表达式 {{属性名}} 时,不要有空格,然后就是在中文状态下输入{{和}},因为我切换到英文输入法输入时,表达式的值没法被值替换掉。
2.3、核心代码
Map<String,Object> params 是word模板导出时,用来装要导出的数据。需要注意的是,构造Map时,注意字段值的判空。由于文件是先按模板生成再下载,所以会生成一个临时的文件在指定位置,如果不需要的话,那就在文件下载之后将其删除。
void testExportData() {
// 从数据库根据id获取数据,这里id我用了一个写死的,具体项目中,id请通过前端传入
ProblemRectify rectifyInfo = problemRectifyService.getCheckRectifyInfo(131L);
// 构造导出数据
Map<String, Object> exportParams = new HashMap<>();
exportParams.put("description", rectifyInfo.getDescription());
exportParams.put("bidName", rectifyInfo.getBidName());
exportParams.put("pileNum", rectifyInfo.getPileNum());
exportParams.put("sourceName",rectifyInfo.getSourceName());
exportParams.put("levelName",rectifyInfo.getLevelName());
exportParams.put("typeName",rectifyInfo.getTypeName());
exportParams.put("sortName",rectifyInfo.getSortName());
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
exportParams.put("checkDate",rectifyInfo.getCheckDate().format(dateTimeFormatter));
exportParams.put("rectifyDate",rectifyInfo.getRectifyDate().format(dateTimeFormatter));
exportParams.put("proOrganizationName",rectifyInfo.getProOrganizationName()));
exportParams.put("findPersonName",rectifyInfo.getFindPersonName());
exportParams.put("liablePersonName",rectifyInfo.getLiablePersonName());
exportParams.put("rectifyStatus",rectifyInfo.getRectifyStatus());
exportParams.put("rectifyCompleteDate",rectifyInfo.getRectifyCompleteDate().format(dateTimeFormatter));
exportParams.put("rectifyPlan", StringUtils.isNotBlank(rectifyInfo.getRectifyPlan()) ? checkRectifyInfo.getRectifyPlan() : "无整改方案");
exportParams.put("rectifyResult",StringUtils.isNotBlank(checkRectifyInfo.getRectifyResult()) ? rectifyInfo.getRectifyResult() : "无整改结果");
// 整改前照片
String picturePathStr = rectifyInfo.getPictures();
List<ImageEntity> pictureImageList = initImageData(picturePathStr);
exportParams.put("pictures",pictureImageList);
// 整改后照片
String rectifyPicturesPathStr = checkRectifyInfo.getRectifyPictures();
List<ImageEntity> rectifyImageList = initImageData(rectifyPicturesPathStr);
exportParams.put("rectifyImages",rectifyImageList);
this.exportWord("exportTemplate/problemExportTemplate.docx","问题详情.docx",exportParams);
System.out.println("文件导出成功!");
}
/****
* 导出word数据
* @param templatePath
* @param fileName
* @param params
*/
private void exportWord(String templatePath, String fileName, Map<String, Object> params) {
Assert.notNull(templatePath, "模板路径不能为空!");
Assert.notNull(fileName, "文件名称不能为空!");
Assert.isTrue(fileName.endsWith(".docx"), "word导出请使用docx格式!");
try {
XWPFDocument doc = WordExportUtil.exportWord07(templatePath, params);
String tempPath = fileName;
FileOutputStream fos = new FileOutputStream(tempPath);
doc.write(fos);
// 设置强制下载不打开
response.setContentType("application/force-download");
// 设置文件名
response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName,"UTF-8"));
OutputStream out = response.getOutputStream();
doc.write(out);
out.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
delFileWord(tempPath,fileName);
}
}
private List<ImageEntity> initImageData(String fileUrl) {
List<ImageEntity> result = new ArrayList<>();
if (StringUtils.isNotBlank(fileUrl)) {
if (fileUrl.indexOf(",") > 0) {
String[] fileUrlArr = fileUrl.split(",");
for (int i = 0 ; i < fileUrlArr.length ; i++) {
byte[] imageData = FDSUtils.downloadFile(fileUrlArr[i]);
ImageEntity item = new ImageEntity();
item.setWidth(300);
item.setHeight(300);
item.setData(imageData);
item.setType(ImageEntity.Data);
result.add(item);
}
} else {
byte[] imageData = FDSUtils.downloadFile(fileUrl);
ImageEntity item = new ImageEntity();
item.setWidth(300);
item.setHeight(300);
item.setData(imageData);
item.setType(ImageEntity.Data);
result.add(item);
}
} else {
ImageEntity item = new ImageEntity();
item.setWidth(300);
item.setHeight(300);
item.setData(null);
item.setType(ImageEntity.Data);
result.add(item);
}
return result;
}
/***
* 删除临时文件
* @param filePath
* @param fileName
*/
private static void delFileWord(String filePath, String fileName){
File file =new File(filePath+fileName);
File file1 =new File(filePath);
file.delete();
file1.delete();
}
注意:这里代码需要注意的两个地方,一是模板路径,二是图片的设置,我这里是通过图片路径获取图片的byte[ ]数据,也就是代码中的fastDFSUtils.downloadFile(fileUrl)。因为项目中使用的fastDFS进行文件的上传和存储,写的有这个工具类。
但是,如果不想通过这种方法,也可以直接使用图片Url的方式,利用ImageEntity下的setUrl方法设置图片的存储的路径,然后通过setType将类型设置为ImageEntity.Url,这种方法我没有测试,大家可以测试一下。