java使用freemaker模版生成docxword文档的解决方案
java使⽤freemaker模版⽣成docxword⽂档的解决⽅案
公司前⼀段时间做的⼀个项⽬中应⽤到了这个技术,需要后台获取数据后在前台直接下载word⽂档,利⽤freemarker模版⽣成的doc⽂档在电脑上可以正常打开,但是发送到⼿机上打开则全部变成“乱码”。但实际上并不是乱码,⽽是xml格式的代码,在⼿机等移动端显⽰不出正常的⽂档信息。之后辗转查询使⽤了很多⽅案,⽐如利⽤poi操作,利⽤jacob进⾏格式转换,但是效果都不尽如⼈意,⽽且处理过程⾮常复杂,浪费了⼤量时间。最终,通过不断摸索,到了⾮常完美的解决⽅案。不需要先⽣成xml格式的doc⽂档再去转换,⽽是戒指在后台利⽤模版添加数据后,利⽤IO流进⾏替换,可以直接⽣成标准的docx⽂档。
⾸先需要理解的⼀点是,docx⽂档本⾝是⼀个压缩⽂件。在利⽤这个⽅案前要先准备好docx模版。在这个docx模版中,将要替换的数据⽤${}的⽅式替换掉,先占好位置。如图:
将docx改后缀名为.zip并解压出来。在word⽂件夹下有⼀个⽂件为、:l
利⽤notpad等⼯具打开此⽂件,将格式修改好,⽐如在xml⽂件中${xxx}分别氛围${ ,  xxx 以及 } 三部分存贮在不同的标签下,要将他们合并到⼀起。
随后将保存好的l和之前定义好的zip⽂件放⼊⼯程的配置⽂件夹下,⽅便引⽤。
核⼼代码如下
Configuration
configuration = new Configuration();
/** 设置编码 **/
/** 我的ftl⽂件是放在D盘的**/
String fileDirectory = "D:/cache/qqChace/T1/xlsx";
/** 加载⽂件 **/
configuration.setDirectoryForTemplateLoading(new File(fileDirectory));
/** 加载模板 **/
玩游戏的电脑配置
Template template = Template("l");
/** 准备数据 **/
Map<String,String> dataMap = new HashMap<>();
/** 在ftl⽂件中有${textDeal}这个标签**/
dataMap.put("id","哈⼠奇");
dataMap.put("number","20");
李小璐演过的电视剧
dataMap.put("language","java,php,python,c++.......");
dataMap.put("example","Hello World!");
/** 指定输出word⽂件的路径 **/
String outFilePath = "D:/cache/qqChace/T1/l";
File docFile = new File(outFilePath);
FileOutputStream fos = new FileOutputStream(docFile);
OutputStreamWriter oWriter = new OutputStreamWriter(fos);
Writer out = new BufferedWriter(new OutputStreamWriter(fos),10240);
template.process(dataMap,out);
if(out != null){
out.close();
}
// ZipUtils 是⼀个⼯具类,主要⽤来替换具体可以看github⼯程
ZipInputStream zipInputStream = ZipUtils.wrapZipInputStream(new FileInputStream(new File("D:/cache/qqChace/T1/xlsx/test.zip")));
ZipOutputStream zipOutputStream = ZipUtils.wrapZipOutputStream(new FileOutputStream(new File("D:/cache/qqChace/T1/xlsx/test.docx")));
String itemname = "l";
word打开是乱码placeItem(zipInputStream, zipOutputStream, itemname, new FileInputStream(new File("D:/cache/qqChace/T1/l")));
System.out.println("success");
想要源码可以去这个地址下载。
上⾯主要解决的问题事⽂字的转换,下⾯要说的关于图⽚的转换问题。
将zip⽂件解压后会发现,l中没有你原本保存在docx中的图⽚,图⽚是在word下的media⽂件夹下。l中或
许只是引⽤。但是仍可以通过这种⽅式,以流的形式替换图⽚。只要保证图⽚的顺序不乱就可以了。
下⾯是我在上⾯这段代码上进⾏的改动
String zipPath =  Class().getResource("/freemarkerDoc.zip").getPath();
try {
ZipInputStream zipInputStream = ZipUtils.wrapZipInputStream(new FileInputStream(new File(zipPath)));
ZipOutputStream zipOutputStream = ZipUtils.wrapZipOutputStream(new FileOutputStream(new File(Name()+"连续⾎氧检测报告            String itemname = "l";
String itemname2 = "word/media/";
String picturePath = BinaryRoorPath("picturePath")+File.Id()+File.Id()+File.separator;            placeItem(zipInputStream, zipOutputStream, itemname, new FileInputStream(new File(url)), itemname2, picturePath);
System.out.println("success");
} catch (Exception e) {
System.out.String());
}
我的docx⽂档中⼯有四张图⽚,所以在zipUtil中要传⼊你要替换上去的图⽚路径。代码如下
public static void replaceItem(ZipInputStream zipInputStream,
public static void replaceItem(ZipInputStream zipInputStream,
ZipOutputStream zipOutputStream,
String itemName,
翡翠的种类InputStream itemInputStream
,
String itemName2,
String picturePath
){
//
if(null == zipInputStream){return;}
if(null == zipOutputStream){return;}
if(null == itemName){return;}
if(null == itemInputStream){return;}
InputStream itemIS = null;
//
ZipEntry entryIn;
try {
while((entryIn = NextEntry())!=null)
{
String entryName =  Name();
ZipEntry entryOut = new ZipEntry(entryName);
// 只使⽤ name
zipOutputStream.putNextEntry(entryOut);
// 缓冲区
byte [] buf = new byte[8*1024];
int len;
//image1.png等是media⽬录下的图⽚名称。
String ima1 = itemName2+"image1.png";
String ima2 = itemName2+"image2.png";
String ima3 = itemName2+"image3.png";
String ima4 = itemName2+"image4.png";
if(entryName.equals(itemName)){
// 使⽤替换流
while((len = (ad(buf))) > 0) {
zipOutputStream.write(buf, 0, len);
}
}else if(entryName.equals(ima1)){
// 使⽤替换流,将需要替换上去的图⽚路径以流的形式获取
itemIS = new FileInputStream(new File(picturePath+"spo2PiePicture.png"));                    while((len = (ad(buf))) > 0) {
zipOutputStream.write(buf, 0, len);
}
}else if(entryName.equals(ima2)){
// 使⽤替换流
itemIS = new FileInputStream(new File(picturePath+"prPiePicture.png"));
while((len = (ad(buf))) > 0) {
zipOutputStream.write(buf, 0, len);
}
}else if(entryName.equals(ima3)){
// 使⽤替换流
itemIS = new FileInputStream(new File(picturePath+"spo2Picture.png"));
while((len = (ad(buf))) > 0) {
zipOutputStream.write(buf, 0, len);
}
}else if(entryName.equals(ima4)){
// 使⽤替换流
itemIS = new FileInputStream(new File(picturePath+"prPicture.png"));
while((len = (ad(buf))) > 0) {
zipOutputStream.write(buf, 0, len);
}房产证
}else {
// 输出普通Zip流
while((len = (ad(buf))) > 0) {
zipOutputStream.write(buf, 0, len);
}
}
}
// 关闭此 entry
zipOutputStream.closeEntry();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//e.printStackTrace();
close(itemInputStream);
close(zipInputStream);
汽车漆面镀膜
close(zipOutputStream);
close(itemIS);
}
}
转换完成后要讲所有的流全部关闭,就完成了。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。