目录
业务场景:在平时的业务开发过程中,用户填写表单后,后台管理员需要将用户填写的数据导出到一个Word表单中。为了提高效率,管理员希望能够批量导出这些数据,而不是一个个地导出。然而,后续发现每个人登录管理员账户都有可能下载这些数据,这可能导致敏感信息的泄露。为了避免这种情况,需要对压缩包进行加密处理。
参考文档:JAVA poi-tl 制作word模板 表格数据行循环 带有复选框勾选的表格_poi-tl 复选框-CSDN博客
那么下述就对上述这三种情况提供接口实例代码实现:
1.模板文件:
2. 制作word模板
模板文件链接:https://pan.baidu.com/s/1Lvo43xkYp6ec2RonHpEeKA
提取码:0617
3.导出word效果
一、将数据导出到指定word模板当中
引入依赖
<!--POI-TL实现数据导出到word模板-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.0.M2</version>
</dependency>
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.9.1</version>
</dependency>
实体类
Voteinfo
/**
* 投票详情
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class VoteInfo {
/** 投票人名称 */
private String voterName;
/** 投票结果 1、同意 2、续议 3、拒绝 4、回避 */
private String voteResult;
/** 投票意见 */
private String voteOpinion;
}
DataForm
/**
* word数据表单
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DataForm {
/** 公司名称 */
private String companyName;
/** 时间-年 */
private String year;
/** 时间-月 */
private String month;
/** 时间-日 */
private String day;
/** 会议主题 */
private String meetingTheme;
/** 会议类型 1、股东会 2、董事会 3、合伙人会 4、其他 */
private String meetingType;
/** 是否通过 1、同意 2、续议 3、拒绝 */
private String passFlag;
/** 汇总意见 所有投票人的意见汇总 */
private String allOpinion;
/** 签名 */
private String sign;
/** 签名日期 yyyy-MM-dd */
private String signDate;
}
接口
@RestController
@Slf4j
@RequestMapping("/")
public class WordController {
//127.0.0.1:8080/generateWordForm
@GetMapping("/generateWordForm")
public void generateWordForm(HttpServletResponse response) throws IOException {
/**
* 1. 获取所有投票人的列表数据
* 此处使用测试数据,实际项目中需要自己从数据库查询
* */
List<VoteInfo> infoList = new ArrayList<VoteInfo>() {{
add(new VoteInfo("张三", "1", "没有意见"));
add(new VoteInfo("李四", "1", "同意"));
add(new VoteInfo("王五", "3", "拒绝,不同意"));
add(new VoteInfo("赵六", "4", "我回避!不做任何评价"));
add(new VoteInfo("刘七", "2", "我认为需要续议,日后再重新讨论"));
}};
StringBuffer allOpinion = new StringBuffer();
for (VoteInfo info : infoList) {
//拼接所有投票人的意见 并换行
allOpinion.append(info.getVoterName() + ":" + info.getVoteOpinion() + "\n");
}
/**
* 2. 获取并设置表单数据 此处为测试数据 meetingType和passFlag默认设置为1
* 复选框 meetingType 会议类型 1、股东会 2、董事会 3、合伙人会 4、其他
* 复选框 passFlag 是否通过 1、同意 2、续议 3、拒绝
* */
DataForm dataForm = new DataForm("大聪明集团", "2024", "1", "1",
"如何成为大聪明", "2", "2",
allOpinion.toString(), "大聪明老总", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
/**
* 3. 数据map 用于渲染word表单的数据 将表单数据对象存入map中
*/
Map<String, Object> dataMap = BeanUtil.beanToMap(dataForm);
//需要循环的表单数据
dataMap.put("dataTable", infoList);
/**
* 4. Configure类是该库中的一个配置类,其作用是提供了一些全局的配置选项
* (1) useSpringEL() 开启El表达式{{ }} word模板中的数据就以这个表达式传递数据 例如:{{companyName}};也可以调用buildGramer("${", "}") 可以修改模板为${}
* (2) bind() 绑定标记需要循环的数据
* (3) 实现表格行循环的策略 HackLoopTableRenderPolicy 不同poi版本实现行循环的策略不一样;当前案例是poi-tl 1.9.1版本
* 1.9.x版本:HackLoopTableRenderPolicy 1.10.x以后的版本:LoopRowTableRenderPolicy
*/
ConfigureBuilder configureBuilder = Configure.builder().useSpringEL().bind("dataTable", new HackLoopTableRenderPolicy());
Configure config = configureBuilder.build();
InputStream is = null;
try {
/**
* 5. word模板渲染数据 wordForm.docx为模板,本案例放在了项目根目录的wordTemplates下
*/
is = new ClassPathResource("wordTemplates/wordForm.docx").getInputStream();
XWPFTemplate template = XWPFTemplate.compile(is, config).render(dataMap);
// 6.生成Word文件到内存中
ByteArrayOutputStream out = new ByteArrayOutputStream();
template.writeAndClose(out);
out.flush();
// 将Word文件转换为字节流并写入到响应对象的输出流中
String fileName = URLEncodeUtil.encode("数据导出到word模板测试");
byte[] data = out.toByteArray();
response.setContentType("application/octet-stream;charset=utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".docx");
response.getOutputStream().write(data);
response.getOutputStream().flush();
} catch (IOException e) {
log.error("生成意见反馈表失败!", e);
} finally {
if (null != is) {
try {
//最后别忘记了关闭流
is.close();
} catch (IOException e) {
log.error("关闭流失败!", e);
}
}
}
}
}
接口测试
postman进行接口调用或直接浏览器访问:
127.0.0.1:8080(你的ip地址:端口号)/generateWordForm
测试结果
二、批量导出多个word并压缩成zip(不加密版本)
引入依赖
引入zip压缩包的依赖
<!-- zip的依赖-->
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>2.6.0</version>
</dependency>
接口
/**
* 批量下载word文件打成zip--不加密版本
*/
@GetMapping("/batchGenerateWordForm")
public void batchGenerateWordForm1(HttpServletResponse response) throws IOException {
// 1. 获取所有投票人的列表数据
List<VoteInfo> infoList = new ArrayList<VoteInfo>() {{
add(new VoteInfo("张三", "1", "没有意见"));
add(new VoteInfo("李四", "1", "同意"));
add(new VoteInfo("王五", "3", "拒绝,不同意"));
add(new VoteInfo("赵六", "4", "我回避!不做任何评价"));
add(new VoteInfo("刘七", "2", "我认为需要续议,日后再重新讨论"));
}};
List<VoteInfo> infoList1 = new ArrayList<VoteInfo>() {{
add(new VoteInfo("张三1", "1", "没有意见"));
add(new VoteInfo("李四1", "1", "同意"));
add(new VoteInfo("王五1", "3", "拒绝,不同意"));
add(new VoteInfo("赵六1", "4", "我回避!不做任何评价"));
add(new VoteInfo("刘七1", "2", "我认为需要续议,日后再重新讨论"));
}};
// 2. 获取并设置表单数据
DataForm dataForm = new DataForm("大聪明集团", "2024", "1", "1",
"如何成为大聪明", "2", "2",
getAllOpinion(infoList), "大聪明老总", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
DataForm dataForm1 = new DataForm("大聪明集团1", "2024", "1", "1",
"如何成为大聪明1", "2", "2",
getAllOpinion(infoList1), "大聪明老总1", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
// 3. 数据map 用于渲染word表单的数据 将表单数据对象存入map中
List<Map<String, Object>> mapList = new ArrayList<>();
Map<String, Object> dataMap = BeanUtil.beanToMap(dataForm);
Map<String, Object> dataMap1 = BeanUtil.beanToMap(dataForm1);
mapList.add(dataMap);
mapList.add(dataMap1);
dataMap.put("dataTable", infoList);
dataMap1.put("dataTable", infoList1);
// 4. 配置全局选项
ConfigureBuilder configureBuilder = Configure.builder().useSpringEL().bind("dataTable", new HackLoopTableRenderPolicy());
Configure config = configureBuilder.build();
// 5. 创建zip文件并加密
String zipFilePath = "download/wordForms.zip";
String password = "lock123";
FileOutputStream fos = new FileOutputStream(zipFilePath);
ZipOutputStream zipOut = new ZipOutputStream(fos);
try {
// 6. 循环渲染word表单数据并写入zip文件中
for (Map<String, Object> data : mapList) {
InputStream is = new ClassPathResource("wordTemplates/wordForm.docx").getInputStream();
XWPFTemplate template = XWPFTemplate.compile(is, config).render(data);
// 7. 将word文件写入zip文件中
ByteArrayOutputStream out = new ByteArrayOutputStream();
template.writeAndClose(out);
out.flush();
byte[] dataBytes = out.toByteArray();
ZipEntry zipEntry = new ZipEntry(data.get("companyName") + ".docx");
zipOut.putNextEntry(zipEntry);
zipOut.write(dataBytes);
zipOut.closeEntry();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 8. 关闭zip文件输出流
zipOut.close();
fos.close();
// 9. 下载zip文件
String fileName = URLEncodeUtil.encode("数据导出到word模板测试");
response.setContentType("application/octet-stream;charset=utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".zip");
FileInputStream fis = new FileInputStream(zipFilePath);
IOUtils.copy(fis, response.getOutputStream());
fis.close();
}
}
private String getAllOpinion(List<VoteInfo> infoList) {
StringBuffer allOpinion = new StringBuffer();
for (VoteInfo info : infoList) {
//拼接所有投票人的意见 并换行
allOpinion.append(info.getVoterName() + ":" + info.getVoteOpinion() + "\n");
}
return allOpinion.toString();
}
接口测试
postman进行接口调用或直接浏览器访问:
127.0.0.1:8080(你的ip地址:端口号)/batchGenerateWordForm
测试结果
三、批量导出多个word并压缩成zip(加密版本)
接口
/**
* 批量下载word文件打成zip--加密版本
*/
@GetMapping("/batchGenerateWordFormEncrypt")
public void batchGenerateWordForm(HttpServletResponse response) throws IOException {
// 1. 获取所有投票人的列表数据
List<VoteInfo> infoList = new ArrayList<VoteInfo>() {{
add(new VoteInfo("张三", "1", "没有意见"));
add(new VoteInfo("李四", "1", "同意"));
add(new VoteInfo("王五", "3", "拒绝,不同意"));
add(new VoteInfo("赵六", "4", "我回避!不做任何评价"));
add(new VoteInfo("刘七", "2", "我认为需要续议,日后再重新讨论"));
}};
List<VoteInfo> infoList1 = new ArrayList<VoteInfo>() {{
add(new VoteInfo("张三1", "1", "没有意见"));
add(new VoteInfo("李四1", "1", "同意"));
add(new VoteInfo("王五1", "3", "拒绝,不同意"));
add(new VoteInfo("赵六1", "4", "我回避!不做任何评价"));
add(new VoteInfo("刘七1", "2", "我认为需要续议,日后再重新讨论"));
}};
// 2. 获取并设置表单数据
DataForm dataForm = new DataForm("大聪明集团", "2024", "1", "1",
"如何成为大聪明", "2", "2",
getAllOpinion(infoList), "大聪明老总", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
DataForm dataForm1 = new DataForm("大聪明集团1", "2024", "1", "1",
"如何成为大聪明1", "2", "2",
getAllOpinion(infoList1), "大聪明老总1", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
// 3. 数据map 用于渲染word表单的数据 将表单数据对象存入map中
List<Map<String, Object>> mapList = new ArrayList<>();
Map<String, Object> dataMap = BeanUtil.beanToMap(dataForm);
Map<String, Object> dataMap1 = BeanUtil.beanToMap(dataForm1);
mapList.add(dataMap);
mapList.add(dataMap1);
dataMap.put("dataTable", infoList);
dataMap1.put("dataTable", infoList1);
// 4. 配置全局选项
ConfigureBuilder configureBuilder = Configure.builder().useSpringEL().bind("dataTable", new HackLoopTableRenderPolicy());
Configure config = configureBuilder.build();
// 5. 创建临时目录并生成Word文件
String tempDir = "download/temp/";
String zipDir = "download/zip/";
String zipFileName = zipDir + "wordForms.zip";
String password = "lock123";
File dir = new File(tempDir);
if (!dir.exists()) {
dir.mkdirs();
}
File zDir = new File(zipDir);
if (!zDir.exists()) {
zDir.mkdirs();
}
List<String> fileNames = new ArrayList<>();
fileNames.add(dataForm.getCompanyName() + ".docx");
fileNames.add(dataForm1.getCompanyName() + ".docx");
try {
// 6. 循环渲染word表单数据并写入临时目录中
for (Map<String, Object> data : mapList) {
InputStream is = new ClassPathResource("wordTemplates/wordForm.docx").getInputStream();
XWPFTemplate template = XWPFTemplate.compile(is, config).render(data);
// 7. 将word文件写入临时目录中
String tmpPath = tempDir + data.get("companyName") + ".docx";
FileOutputStream fos = new FileOutputStream(tmpPath);
template.writeAndClose(fos);
fos.close();
}
// 7. 创建加密的Zip文件
File zipTemFile = new File(zipFileName);
ZipFile zipFile = new ZipFile(zipTemFile,password.toCharArray());
ZipParameters zipParams = new ZipParameters();
zipParams.setCompressionMethod(CompressionMethod.DEFLATE); // 设置压缩方法为 DEFLATE
zipParams.setCompressionLevel(CompressionLevel.NORMAL); // 设置压缩级别为 NORMAL
zipParams.setEncryptFiles(true); // 设置为加密模式
zipParams.setEncryptionMethod(EncryptionMethod.AES); // 设置加密算法为 AES
zipParams.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_256); // 设置 AES 密钥长度为 256 位
File srcFile = new File(tempDir);
if (srcFile.isDirectory()) {
for (File file : srcFile.listFiles()) {
zipFile.addFile(file, zipParams);
}
}
// 8. 下载加密的zip文件
String fileName = URLEncodeUtil.encode("数据导出到word模板测试");
response.setContentType("application/octet-stream;charset=utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".zip");
FileInputStream encryptedFis = new FileInputStream(zipTemFile);
IOUtils.copy(encryptedFis, response.getOutputStream());
encryptedFis.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 9. 删除临时文件
delFile(fileNames, zipFileName, tempDir);
File file1 = new File(zipDir);
file1.delete();
}
}
private static void delFile(List<String> fileName, String zipFileName, String tempDir) {
for (String name : fileName) {
File file = new File(tempDir + name);
file.delete();
}
File file = new File(zipFileName);
file.delete();
File file1 = new File(tempDir);
file1.delete();
}
private String getAllOpinion(List<VoteInfo> infoList) {
StringBuffer allOpinion = new StringBuffer();
for (VoteInfo info : infoList) {
//拼接所有投票人的意见 并换行
allOpinion.append(info.getVoterName() + ":" + info.getVoteOpinion() + "\n");
}
return allOpinion.toString();
}
接口测试
postman进行接口调用或直接浏览器访问:
127.0.0.1:8080(你的ip地址:端口号)/batchGenerateWordFormEncrypt
测试结果
通过上述的代码,我们就能实现将数据导出到指定word模板当中,批量导出多个word并压缩成zip并加密。