RT,耗费了博主半个月的时间才挤出来的成果,在此记录下开发过程。
博主的另一篇文章改用mht模板导出了包含图片的word:
https://blog.csdn.net/u011099093/article/details/85318212
一、创建freemark模板
首先在web項目中指定目錄下創建一個HTML格式的freemarker模板:
<!DOCTYPE html>
<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml"
xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta charset="UTF-8">
<!--[if gte mso 9]>
<xml>
<w:WordDocument>
<w:View>Print</w:View>
<w:TrackMoves>false</w:TrackMoves>
<w:TrackFormatting/>
<w:ValidateAgainstSchemas/>
<w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid>
<w:IgnoreMixedContent>false</w:IgnoreMixedContent>
<w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText>
<w:DoNotPromoteQF/>
<w:LidThemeOther>EN-US</w:LidThemeOther>
<w:LidThemeAsian>ZH-CN</w:LidThemeAsian>
<w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript>
<w:Compatibility>
<w:BreakWrappedTables/>
<w:SnapToGridInCell/>
<w:WrapTextWithPunct/>
<w:UseAsianBreakRules/>
<w:DontGrowAutofit/>
<w:SplitPgBreakAndParaMark/>
<w:DontVertAlignCellWithSp/>
<w:DontBreakConstrainedForcedTables/>
<w:DontVertAlignInTxbx/>
<w:Word11KerningPairs/>
<w:CachedColBalance/>
<w:UseFELayout/>
</w:Compatibility>
<w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel>
<m:mathPr>
<m:mathFont m:val="Cambria Math"/>
<m:brkBin m:val="before"/>
<m:brkBinSub m:val="--"/>
<m:smallFrac m:val="off"/>
<m:dispDef/>
<m:lMargin m:val="0"/>
<m:rMargin m:val="0"/>
<m:defJc m:val="centerGroup"/>
<m:wrapIndent m:val="1440"/>
<m:intLim m:val="subSup"/>
<m:naryLim m:val="undOvr"/>
</m:mathPr>
</w:WordDocument>
</xml><![endif]-->
<style>
<!--
/*Page Definitions*/
@page WordSection1 {
size: 595.3pt 841.9pt;
margin: 72.0pt 1.0cm 72.0pt 1.0cm;
mso-header-margin: 42.55pt;
mso-footer-margin: 49.6pt;
mso-paper-source: 0;
}
div.WordSection1 {
page: WordSection1
}
-->
</style>
<title>导出html格式Word演示</title>
</head>
<body>
<div class="WordSection1">
<div>
<h1 style="font-size: 32px;font-weight: bold;padding:0px 4px 0px 0px;text-align:center;margin:0px 0px 20px;">
<span style="font-size: 22pt;font-family: 宋体,SimSun;"><strong>Word导出演示</strong></span>
</h1>
</div>
<p style="text-indent: 32px">
<span style="font-size:21px;font-family: 宋体">${basicShow!}</span>
</p>
<table class="MsoNormalTable"
style="mso-cellspacing: 0cm;mso-table-layout-alt: fixed;
border:outset black 1.0pt;mso-border-alt: outset black .75pt;mso-yfti-tbllook:1184;mso-padding-alt: 0cm 0cm 0cm 0cm"
border="1" cellpadding="0" cellspacing="0" style="border-color:black;">
<tbody>
<tr style="height:39px" class="firstRow">
<td width="120" style="padding: 0px 7px;">
<p style="text-align: center">
<strong><span style="font-size: 16px;font-family: 仿宋;">姓名</span></strong>
</p>
</td>
<td width="120" style="padding: 0px 7px;">
<p style="text-align: center">
<strong><span style="font-size: 16px;font-family: 仿宋;">年龄</span></strong>
</p>
</td>
<td width="120" style="padding: 0px 7px;">
<p style="text-align: center">
<strong><span style="font-size: 16px;font-family: 仿宋;">性别</span></strong>
</p>
</td>
</tr>
<#if lists??>
<#list rows as lists>
<tr style="height:39px" class="firstRow">
<td width="120" style="padding: 0px 7px;">
<p style="text-align: center">
<span style="font-size: 16px;font-family: 仿宋;">${rows.name!}</span>
</p>
</td>
<td width="120" style="padding: 0px 7px;">
<p style="text-align: center">
<span style="font-size: 16px;font-family: 仿宋;">${rows.age!}</span>
</p>
</td>
<td width="120" style="padding: 0px 7px;">
<p style="text-align: center">
<span style="font-size: 16px;font-family: 仿宋;">${rows.gender!}</span>
</p>
</td>
</tr>
</#list>
</#if>
</tbody>
</table>
</div>
</body>
</html>
其中控制打開后展示為頁面視圖的是這一段:
<!--[if gte mso 9]>
...
<--主要是下面这一行-->
<w:View>Print</w:View>
...
<![endif]-->
百度了好久都沒有百度到相關模板,就寫了個demo先使用html代碼生成一個word,再在word中修改格式,保存。
然後使用NodePad打開保存后的doc文檔分析其中的結構才拼凑成一個格式比較像樣的模板。
控制頁邊距的是以下這一段:
<!-- /*Page Definitions*/ @page WordSection1 { size: 595.3pt 841.9pt; margin: 72.0pt 1.0cm 72.0pt 1.0cm; mso-header-margin: 42.55pt; mso-footer-margin: 49.6pt; mso-paper-source: 0; } div.WordSection1 { page: WordSection1 } -->
使用${param!}引入參數,使用<#if lists??><#list rows as lists></#list></#if>遍歷多條數據插入到模板中;
二、导入数据到模板并生成word
@ResponseBody
@RequestMapping("/exportWord")
public Map<String,String> exportWord(HttpServletRequest request){
try {
//遍历獲取所有參數
Enumeration<String> enu = request.getParameterNames();
String paraName = null;
Map<String, String> parameters = new HashMap<>();
while (enu.hasMoreElements()) {
paraName = enu.nextElement();
parameters.put(paraName, request.getParameter(paraName));
}
//準備一段假數據
Map<String, Object> map = new HashMap<>();
map.put("basicShow", "這裏展示一段文字。。。啦啦啦");
List<User> list = new ArrayList<>();
list.add(new User() {{
setAge(18);
setGender("男");
setName("趙康");
}});
list.add(new User() {{
setAge(34);
setGender("男");
setName("劉天");
}});
list.add(new User() {{
setAge(23);
setGender("女");
setName("李逵");
}});
map.put("lists", list);
String path = request.getRealPath("/");//獲取項目的根目錄
ServletContext context = request.getServletContext();
String filepath = createDoc(path, context, map, "wordOfHtml.ftl", "测试word文档.doc");
Map<String, String> resultMap = new HashMap<>();
resultMap.put("filepath", filepath);
return resultMap;
}catch(Exception e){
e.printStackTrace();
}
return null;
}
private String createDoc(String path, ServletContext context, Map<String, Object> data, String templateName, String docName) {
long startTime=System.currentTimeMillis();
System.out.println("生成word开始。。。");
Configuration configurationc=new Configuration();
//设置模板编码格式
configurationc.setDefaultEncoding("utf-8");
//设置模板存放的路径
configurationc.setServletContextForTemplateLoading(context,"freemarkTemplate");
Template template=null;
String filepath=null;
String htmlpath=null;
try{
//获取模板设置编码类型
template=configurationc.getTemplate(templateName,"UTF-8");
//设置生成word文件的存放路径
filepath= path+"export"+File.separator;//+"test.doc";
File file=new File(filepath);
if(!file.exists()){
// file.createNewFile();
file.mkdirs();
}
filepath+=docName;
Writer bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filepath),"utf-8"));
// BufferedWriter bw=new BufferedWriter(new FileWriter(filepath));
//替换模板中的占位符并输出
template.process(data,bw, ObjectWrapper.BEANS_WRAPPER);
return filepath;
}catch (Exception e){
e.printStackTrace();
}finally {
long end=System.currentTimeMillis();
System.out.println("用时:"+(end-startTime)/1000+"秒;");
System.out.println("生成word结束,开始下载。。。");
}
return null;
}
这里为了测试,我直接造的假数据放到Map中,替换模板中对应的参数;
三、导出word
@ResponseBody
@RequestMapping("/downloadWord")
public void downloadWord(HttpServletResponse response,String filepath){
OutputStream os=null;
FileInputStream inputStream=null;
System.out.println("开始下载。。。");
try{
File file=new File(filepath);
String filename=new String(file.getName().getBytes("GB2312"),"ISO8859-1");
//设置输出文件类型为 word.doc
response.setContentType("application/msword");
//设置文件名
response.setHeader("Content-Disposition","attachment;filename="+filename);
String len=String.valueOf(file.length());
response.setHeader("Content-length",len);
os=response.getOutputStream();
inputStream=new FileInputStream(file);
byte[] bytes=new byte[1024];
int i;
while((i=inputStream.read(bytes))!=-1){
os.write(bytes,0,i);
}
os.flush();
boolean b=file.delete();
System.out.println(b+"----------");
}catch(Exception e){
e.printStackTrace();
}
finally {
try {
if (inputStream != null) {
inputStream.close();
}
if(os!=null){
os.close();
}
}catch(Exception e){
e.printStackTrace();
}
System.out.println("下载完成。。。");
}
}
前台js:
$.ajax({
url: _ctx+'/report/common/exportWord',
data: {},
type: 'post',
dataType: 'json',
success: function (obj) {
var filepath=obj.filepath;
console.info(filepath)
if(filepath!=null){
var encode2=encodeURIComponent(filepath);
console.info(encode2)
console.log(_ctx+'/report/common/downloadWord?'+(new Date().getTime())+"&filepath="+encode2)
window.location.href=_ctx+'/report/common/downloadWord?'+(new Date().getTime())+"&filepath="+encode2;
}else{
alert("下载word失败!");
}
}
})
效果:
使用nodepad打开word,发现其实格式还是html:
所以如果需要什么样式可以直接在word中修改,然后保存,使用记事本打开就可以看到对应的样式,希望对各位小伙伴有所帮助!
-------------------------------------------------华丽的分割线-----------------------------------------------------
(原创文档,转载请注明出处。)