1. 依赖
<!--FreeMarker-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
2. 模板
-
需要放图片的位置,直接把图片粘贴进去调整好大小。
<pkg:part pkg:name="/word/media/image1.jpeg" pkg:contentType="image/jpeg"> <pkg:binaryData>${image}</pkg:binaryData> </pkg:part>
-
doc或docx文件另存为xml,然后将xml文件保存到项目模板位置。
-
pkg:binaryData原本的Base64编码</pkg:binaryData>,替换为${变量名}
3. 具体实现
- 将需要填充的数据查询,填充模板,本地生成,返回流给前端,浏览器自动下载。
@GetMapping("/exportPersonalInformation")
@ApiOperation("廉政档案模板导出")
@LogAnnotation("廉政档案模板导出")
public Result exportPersonalInformation(HttpServletResponse response, String oid) {
try {
Map<String,Object> map = personalInformationService.exportPersonalInformation(oid);
//创建文件名称 +String.valueOf(System.currentTimeMillis())
String fileName = "D:\\" + File.separator + "干部任免审批表.doc";
String templateFileName = this.getClass().getResource("/").getPath() + "templates" + File.separator;
//1、创建Configuration对象,传参freemarker的版本号
Configuration configuration = new Configuration(Configuration.VERSION_2_3_22);
configuration.setClassicCompatible(true); //解决空值问题,必不可少
configuration.setDefaultEncoding("UTF-8");
//2、设置模板文件所在的路径
// configuration.setClassForTemplateLoading(this.getClass(), "E:\\workspace\\Java\\company\\cg-server-project\\enterprise-vertical\\vertical-system\\src\\main\\resources\\templates\\leader.ftl");//File.separator
//3、设置模板文件所用的字符集,一般设置为utf-8
configuration.setDefaultEncoding("utf-8");
// 设置存放路径
configuration.setDirectoryForTemplateLoading(new File(templateFileName));//File.separator
//4、根据获取到的名称加载一个模板,获取一个模板对象'
Template template = configuration.getTemplate("personalInfomation.xml", "utf-8");
//6、创建一个Writer对象,指定生成的文件名
File outFile = new File(fileName);
//若是路径不存在则创建
if (!outFile.getParentFile().exists()) {
outFile.getParentFile().mkdirs();
}
Writer out = null;
FileOutputStream fos = null;
try {
fos = new FileOutputStream(outFile);
OutputStreamWriter oWriter = new OutputStreamWriter(fos, "UTF-8");
//这个地方对流的编码不可或缺,使用main()单独调用时,应该可以,但是如果是web请求导出时导出后word文档就会打不开,并且包XML文件错误。主要是编码格式不正确,无法解析。
//out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile)));
out = new BufferedWriter(oWriter);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
template.process(map, out);
out.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
response.setContentType("application/force-download");// 设置强制下载不打开
response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode("干部任免审批表.doc", "UTF-8"));// 设置文件名
byte[] buffer = new byte[1024];
FileInputStream fis = null;
BufferedInputStream bis = null;
try {
fis = new FileInputStream(outFile);
bis = new BufferedInputStream(fis);
OutputStream os = response.getOutputStream();
int i = bis.read(buffer);
while (i != -1) {
os.write(buffer, 0, i);
i = bis.read(buffer);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
outFile.delete();
}
return null;
} catch (Exception e) {
log.error("廉政档案模板导出失败", e);
return Result.error().message("廉政档案模板导出失败");
}
}
-
获得图片的base64码(我的有解密,正常用下面的就行。
//获得图片的base64码 public static String getImageBase(String src) throws Exception { if (src == null || src == "") { return ""; } File file = new File(src); if (!file.exists()) { return ""; } InputStream in = null; byte[] data = null; try { in = new FileInputStream(file); data = new byte[in.available()]; in.read(data); in.close(); } catch (IOException e) { e.printStackTrace(); } BASE64Encoder encoder = new BASE64Encoder(); return encoder.encode(data); }
-
遍历表格我用的 stringBuffer 拼接的数据,仅供参考。
@Override public Map<String, Object> exportPersonalInformation(String oid) throws IOException { HashMap<String, Object> map = new HashMap<>(); LzdaPersonalInformation personalInformation = personalInformationMapper.selectById(oid); if (personalInformation != null) { SimpleDateFormat ymdFormat = new SimpleDateFormat("yyyy年MM月dd日"); // 姓名 map.put("personName", personalInformation.getPersonName()); // 性别 map.put("sexName", personalInformation.getSexName()); // 出生年月 if (personalInformation.getBirthday() != null) { map.put("birthday", ymdFormat.format(personalInformation.getBirthday())); } else { map.put("birthday", ""); } // 民族 map.put("nationName", personalInformation.getNationName()); // 籍贯 map.put("censusRegisterProvinceName", personalInformation.getCensusRegisterProvinceName()); // 出生地 map.put("birthplace", personalInformation.getBirthplace()); // 入党时间 if (personalInformation.getPartyTime() != null) { map.put("partyTime", ymdFormat.format(personalInformation.getPartyTime())); } else { map.put("partyTime", ""); } if (personalInformation.getWorkTime() != null) { // 参加工作时间 map.put("workTime", ymdFormat.format(personalInformation.getWorkTime())); } else { map.put("workTime", ""); } // 健康状况 map.put("healthConditionNameOne", personalInformation.getHealthConditionNameOne()); // 专业技术职务 map.put("professionalTechnical", personalInformation.getProfessionalTechnical()); // 熟悉专业有何专长 map.put("specialSkill", personalInformation.getSpecialSkill()); // 学历 全日制 map.put("allEducationBackgroundFirstName", personalInformation.getAllEducationBackgroundFirstName()); map.put("allEducationBackgroundSecondName", personalInformation.getAllEducationBackgroundSecondName()); // 学历 在职教育 map.put("inEducationBackgroundFirstName", personalInformation.getInEducationBackgroundFirstName()); map.put("inEducationBackgroundSecondName", personalInformation.getInEducationBackgroundSecondName()); // 全日制 毕业院校系及专业 map.put("allEducationGraduateInstitutions", personalInformation.getAllEducationGraduateInstitutions()); // 在职教育 毕业院校系及专业 map.put("inEducationGraduateInstitutions", personalInformation.getInEducationGraduateInstitutions()); // 现任职务 map.put("presentOccupation", personalInformation.getPresentOccupation()); // 拟任职务 map.put("designatedPosition", personalInformation.getDesignatedPosition()); // 拟免职务 map.put("toFreeOccupation", personalInformation.getToFreeOccupation()); // 简历 map.put("resume", personalInformation.getResume()); // 奖惩情况 map.put("rewardsPunishment", personalInformation.getRewardsPunishment()); // 年度考核结果 map.put("annualAssessment", personalInformation.getAnnualAssessment()); // 任免理由 map.put("appointReason", personalInformation.getAppointReason()); // 家庭成员及重要社会关系 List<LzdaFamilyRelationInfo> familyRelationInfos = familyRelationInfoMapper.selectList(Wrappers.<LzdaFamilyRelationInfo>lambdaQuery().eq(LzdaFamilyRelationInfo::getBusinessId, oid).orderByAsc(LzdaFamilyRelationInfo::getSerialNumber)); if (CollectionUtils.isNotEmpty(familyRelationInfos)) { StringBuffer family = new StringBuffer(); List<LzdaFamilyRelationInfo> familyList = new ArrayList<>(); familyList.addAll(familyRelationInfos); // 不到6行添加空数据 if (familyRelationInfos.size() < 6) { for (int i = 0; i < 7 - familyRelationInfos.size(); i++) { LzdaFamilyRelationInfo lzdaFamilyRelationInfo = new LzdaFamilyRelationInfo(); // 称谓 lzdaFamilyRelationInfo.setFamilyRelationAppellation(""); // 姓名 lzdaFamilyRelationInfo.setFamilyName(""); // 年龄 lzdaFamilyRelationInfo.setAge(""); // 政治面貌 lzdaFamilyRelationInfo.setFamilyPartyName(""); // 工作单位及职务 lzdaFamilyRelationInfo.setFamilyWorkUnit(""); familyList.add(lzdaFamilyRelationInfo); } } for (LzdaFamilyRelationInfo lzdaFamilyRelationInfo : familyList) { family.append("<w:tr>\n" + " <w:tblPrEx>\n" + " <w:tblBorders>\n" + " <w:top w:val=\"single\" w:color=\"auto\" w:sz=\"12\" w:space=\"0\"/>\n" + " <w:left w:val=\"single\" w:color=\"auto\" w:sz=\"12\" w:space=\"0\"/>\n" + " <w:bottom w:val=\"single\" w:color=\"auto\" w:sz=\"12\" w:space=\"0\"/>\n" + " <w:right w:val=\"single\" w:color=\"auto\" w:sz=\"12\" w:space=\"0\"/>\n" + " <w:insideH w:val=\"single\" w:color=\"auto\" w:sz=\"8\" w:space=\"0\"/>\n" + " <w:insideV w:val=\"single\" w:color=\"auto\" w:sz=\"8\" w:space=\"0\"/>\n" + " </w:tblBorders>\n" + " <w:tblCellMar>\n" + " <w:top w:w=\"0\" w:type=\"dxa\"/>\n" + " <w:left w:w=\"0\" w:type=\"dxa\"/>\n" + " <w:bottom w:w=\"0\" w:type=\"dxa\"/>\n" + " <w:right w:w=\"0\" w:type=\"dxa\"/>\n" + " </w:tblCellMar>\n" + " </w:tblPrEx>\n" + " <w:trPr>\n" + " <w:cantSplit/>\n" + " <w:trHeight w:val=\"697\" w:hRule=\"exact\"/>\n" + " </w:trPr>\n" + " <w:tc>\n" + " <w:tcPr>\n" + " <w:tcW w:w=\"742\" w:type=\"dxa\"/>\n" + " <w:vMerge w:val=\"continue\"/>\n" + " <w:noWrap w:val=\"0\"/>\n" + " <w:textDirection w:val=\"tbRlV\"/>\n" + " <w:vAlign w:val=\"center\"/>\n" + " </w:tcPr>\n" + " <w:p>\n" + " <w:pPr>\n" + " <w:spacing w:line=\"0\" w:lineRule=\"atLeast\"/>\n" + " <w:ind w:left=\"113\" w:right=\"113\"/>\n" + " <w:jc w:val=\"center\"/>\n" + " <w:rPr>\n" + " <w:rFonts w:hint=\"eastAsia\"/>\n" + " <w:sz w:val=\"24\"/>\n" + " </w:rPr>\n" + " </w:pPr>\n" + " </w:p>\n" + " </w:tc>\n" + "<w:tc>\n" + " <w:tcPr>\n" + " <w:tcW w:w=\"1092\" w:type=\"dxa\"/>\n" + " <w:noWrap w:val=\"0\"/>\n" + " <w:tcMar>\n" + " <w:top w:w=\"0\" w:type=\"dxa\"/>\n" + " <w:left w:w=\"60\" w:type=\"dxa\"/>\n" + " <w:bottom w:w=\"0\" w:type=\"dxa\"/>\n" + " <w:right w:w=\"40\" w:type=\"dxa\"/>\n" + " </w:tcMar>\n" + " <w:vAlign w:val=\"center\"/>\n" + " </w:tcPr>\n" + " <w:p>\n" + " <w:pPr>\n" + " <w:spacing w:line=\"320\" w:lineRule=\"exact\"/>\n" + " <w:jc w:val=\"center\"/>\n" + " <w:rPr>\n" + " <w:rFonts w:hint=\"eastAsia\" w:eastAsia=\"宋体\"/>\n" + " <w:szCs w:val=\"28\"/>\n" + " <w:lang w:val=\"en-US\" w:eastAsia=\"zh-CN\"/>\n" + " </w:rPr>\n" + " </w:pPr>\n" + " <w:r>\n" + " <w:rPr>\n" + " <w:rFonts w:hint=\"eastAsia\"/>\n" + " <w:szCs w:val=\"28\"/>\n" + " <w:lang w:val=\"en-US\" w:eastAsia=\"zh-CN\"/>\n" + " </w:rPr>\n" + " <w:t>" + lzdaFamilyRelationInfo.getFamilyRelationAppellation() + "</w:t>\n" + " </w:r>\n" + " </w:p>\n" + "</w:tc>\n" + "<w:tc>\n" + " <w:tcPr>\n" + " <w:tcW w:w=\"1274\" w:type=\"dxa\"/>\n" + " <w:noWrap w:val=\"0\"/>\n" + " <w:tcMar>\n" + " <w:top w:w=\"0\" w:type=\"dxa\"/>\n" + " <w:left w:w=\"60\" w:type=\"dxa\"/>\n" + " <w:bottom w:w=\"0\" w:type=\"dxa\"/>\n" + " <w:right w:w=\"40\" w:type=\"dxa\"/>\n" + " </w:tcMar>\n" + " <w:vAlign w:val=\"center\"/>\n" + " </w:tcPr>\n" + " <w:p>\n" + " <w:pPr>\n" + " <w:kinsoku w:val=\"0\"/>\n" + " <w:spacing w:line=\"320\" w:lineRule=\"exact\"/>\n" + " <w:jc w:val=\"center\"/>\n" + " <w:rPr>\n" + " <w:rFonts w:hint=\"eastAsia\" w:eastAsia=\"宋体\"/>\n" + " <w:szCs w:val=\"28\"/>\n" + " <w:lang w:val=\"en-US\" w:eastAsia=\"zh-CN\"/>\n" + " </w:rPr>\n" + " </w:pPr>\n" + " <w:r>\n" + " <w:rPr>\n" + " <w:rFonts w:hint=\"eastAsia\"/>\n" + " <w:szCs w:val=\"28\"/>\n" + " <w:lang w:val=\"en-US\" w:eastAsia=\"zh-CN\"/>\n" + " </w:rPr>\n" + " <w:t>" + lzdaFamilyRelationInfo.getFamilyName() + "</w:t>\n" + " </w:r>\n" + " </w:p>\n" + "</w:tc>\n" + "<w:tc>\n" + " <w:tcPr>\n" + " <w:tcW w:w=\"574\" w:type=\"dxa\"/>\n" + " <w:noWrap w:val=\"0\"/>\n" + " <w:tcMar>\n" + " <w:top w:w=\"0\" w:type=\"dxa\"/>\n" + " <w:left w:w=\"0\" w:type=\"dxa\"/>\n" + " <w:bottom w:w=\"0\" w:type=\"dxa\"/>\n" + " <w:right w:w=\"0\" w:type=\"dxa\"/>\n" + " </w:tcMar>\n" + " <w:vAlign w:val=\"center\"/>\n" + " </w:tcPr>\n" + " <w:p>\n" + " <w:pPr>\n" + " <w:spacing w:line=\"320\" w:lineRule=\"exact\"/>\n" + " <w:jc w:val=\"center\"/>\n" + " <w:rPr>\n" + " <w:rFonts w:hint=\"eastAsia\" w:eastAsia=\"宋体\"/>\n" + " <w:szCs w:val=\"28\"/>\n" + " <w:lang w:val=\"en-US\" w:eastAsia=\"zh-CN\"/>\n" + " </w:rPr>\n" + " </w:pPr>\n" + " <w:r>\n" + " <w:rPr>\n" + " <w:rFonts w:hint=\"eastAsia\"/>\n" + " <w:szCs w:val=\"28\"/>\n" + " <w:lang w:val=\"en-US\" w:eastAsia=\"zh-CN\"/>\n" + " </w:rPr>\n" + " <w:t>" + lzdaFamilyRelationInfo.getAge() + "</w:t>\n" + " </w:r>\n" + " </w:p>\n" + "</w:tc>\n" + "<w:tc>\n" + " <w:tcPr>\n" + " <w:tcW w:w=\"1008\" w:type=\"dxa\"/>\n" + " <w:noWrap w:val=\"0\"/>\n" + " <w:tcMar>\n" + " <w:top w:w=\"0\" w:type=\"dxa\"/>\n" + " <w:left w:w=\"60\" w:type=\"dxa\"/>\n" + " <w:bottom w:w=\"0\" w:type=\"dxa\"/>\n" + " <w:right w:w=\"40\" w:type=\"dxa\"/>\n" + " </w:tcMar>\n" + " <w:vAlign w:val=\"center\"/>\n" + " </w:tcPr>\n" + " <w:p>\n" + " <w:pPr>\n" + " <w:spacing w:line=\"320\" w:lineRule=\"exact\"/>\n" + " <w:jc w:val=\"center\"/>\n" + " <w:rPr>\n" + " <w:rFonts w:hint=\"eastAsia\" w:eastAsia=\"宋体\"/>\n" + " <w:szCs w:val=\"28\"/>\n" + " <w:lang w:val=\"en-US\" w:eastAsia=\"zh-CN\"/>\n" + " </w:rPr>\n" + " </w:pPr>\n" + " <w:r>\n" + " <w:rPr>\n" + " <w:rFonts w:hint=\"eastAsia\"/>\n" + " <w:szCs w:val=\"28\"/>\n" + " <w:lang w:val=\"en-US\" w:eastAsia=\"zh-CN\"/>\n" + " </w:rPr>\n" + " <w:t>" + lzdaFamilyRelationInfo.getFamilyPartyName() + "</w:t>\n" + " </w:r>\n" + " </w:p>\n" + "</w:tc>\n" + "<w:tc>\n" + " <w:tcPr>\n" + " <w:tcW w:w=\"4731\" w:type=\"dxa\"/>\n" + " <w:gridSpan w:val=\"3\"/>\n" + " <w:noWrap w:val=\"0\"/>\n" + " <w:tcMar>\n" + " <w:top w:w=\"0\" w:type=\"dxa\"/>\n" + " <w:left w:w=\"60\" w:type=\"dxa\"/>\n" + " <w:bottom w:w=\"0\" w:type=\"dxa\"/>\n" + " <w:right w:w=\"40\" w:type=\"dxa\"/>\n" + " </w:tcMar>\n" + " <w:vAlign w:val=\"center\"/>\n" + " </w:tcPr>\n" + " <w:p>\n" + " <w:pPr>\n" + " <w:spacing w:line=\"320\" w:lineRule=\"exact\"/>\n" + " <w:jc w:val=\"left\"/>\n" + " <w:rPr>\n" + " <w:rFonts w:hint=\"eastAsia\" w:eastAsia=\"宋体\"/>\n" + " <w:szCs w:val=\"28\"/>\n" + " <w:lang w:val=\"en-US\" w:eastAsia=\"zh-CN\"/>\n" + " </w:rPr>\n" + " </w:pPr>\n" + " <w:r>\n" + " <w:rPr>\n" + " <w:rFonts w:hint=\"eastAsia\"/>\n" + " <w:szCs w:val=\"28\"/>\n" + " <w:lang w:val=\"en-US\" w:eastAsia=\"zh-CN\"/>\n" + " </w:rPr>\n" + " <w:t>" + lzdaFamilyRelationInfo.getFamilyWorkUnit() + "</w:t>\n" + " </w:r>\n" + " </w:p>\n" + "</w:tc>" + "</w:tr>"); } map.put("family", family); } // 呈报单位 map.put("regionName", SessionUtils.getRegionName()); map.put("date", ymdFormat.format(new Date())); // 填表人 map.put("preparer", SessionUtils.getUserName()); // 图片 MultiMediaInfo multiMediaInfo = multiMediaInfoMapper.selectOne(Wrappers.<MultiMediaInfo>lambdaQuery().eq(MultiMediaInfo::getBusinessOid, oid)); // 解密图片文件,并生成64编码 if (multiMediaInfo != null) { byte[] decrypt = MultiMediaUtils.decrypt(multiMediaConfig.getProfile(), multiMediaInfo.getFilePath(), multiMediaConfig.getSecretKey()); BASE64Encoder encoder = new BASE64Encoder(); if (decrypt != null) { map.put("image", encoder.encode(decrypt)); } else { map.put("image", ""); } } } return map; }