使用freemarker 模板生成Excel和Word文件

使用freemarker 模板生成文件Excel和Word文件
freemarker的常用语法
1. 引入依赖的包

pom.xml文件


<dependency>
   <groupId>org.freemarkergroupId>
   <artifactId>freemarkerartifactId>
   <version>2.3.30version>
dependency>
2. Freemarker配置

FreemarkerUtil.java

package com.jeesite.modules.jss.Utils;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;

public class FreemarkerUtil {

    public Template getTemplate(String name) {
        try {
            这里是对应的你使用jar包的版本号:2.3.30
            Configuration configuration = new Configuration(Configuration.VERSION_2_3_30);
            // 空值时不显示
            configuration.setClassicCompatible(true);
            //第二个参数 为你对应存放.ftl文件的包名  (template在resource文件夹下)
            configuration.setClassForTemplateLoading(this.getClass(), "/template");
            Template template = configuration.getTemplate(name);
            return template;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}
3. ftl模板文件的生成

在excel/word文件里画好模板,将excel/word文件另存为.xml文件。在template文件夹下新建模板文件,后缀为.ftl,然后将.xml文件的内容复制到模板文件中。
使用freemarker 模板生成Excel和Word文件_第1张图片
使用freemarker 模板生成Excel和Word文件_第2张图片

循环数据集行的部分,在ftl文件里使用<#list>实现:

excel循环行:

使用freemarker 模板生成Excel和Word文件_第3张图片

word循环行:

<#list priceInfo.jssProductList as product>
    <w:tr w:rsidR="003F3BC7" w:rsidRPr="00F12F15" w14:paraId="3C7E34E4" w14:textId="77777777" w:rsidTr="00E74DC5">
        <w:tc>
            <w:tcPr>
                <w:tcW w:w="1134" w:type="dxa"/>
            w:tcPr>
            <w:p w14:paraId="0E684322" w14:textId="6CB43827" w:rsidR="003F3BC7" w:rsidRPr="00F12F15" w:rsidRDefault="00FA3CA0" w:rsidP="003F3BC7">
                <w:pPr>
                    <w:jc w:val="left"/>
                    <w:rPr>
                        <w:sz w:val="18"/>
                        <w:lang w:eastAsia="zh-CN"/>
                    w:rPr>
                w:pPr>
                <w:r>
                    <w:rPr>
                        <w:rFonts w:ascii="等线" w:eastAsia="等线" w:hAnsi="等线" w:hint="eastAsia"/>
                        <w:sz w:val="18"/>
                        <w:lang w:eastAsia="zh-CN"/>
                    w:rPr>
                    <w:t>${product.itemDesc}w:t>
                w:r>
            w:p>
        w:tc>
    w:tr>
#list>
金额格式化: ${(product.price?string('0.00'))!'0.00'}
日期格式化: ${contract.wantedDeliveryDate?string('yyyy-MM-dd')}

值为NULL时格式转化会出错,加上 “!", !"后的内容表示数据为null时显示的值

<#list jssOrders.jssProductList as product>
<#list product.jssDeviceList as device>
<#if (device_index == 0)>
<Row ss:AutoFitHeight="0">
    <Cell ss:MergeDown="${product.jssDeviceList?size-1}" ss:StyleID="m3012774613960"><Data ss:Type="String">${product.itemDesc}Data>Cell>
    <Cell ss:MergeDown="${product.jssDeviceList?size-1}" ss:StyleID="m3012774613980"><Data ss:Type="String">${product.model}Data>Cell>
    <Cell ss:MergeDown="${product.jssDeviceList?size-1}" ss:StyleID="m3012774614020"><Data ss:Type="String">${product.qty}Data>Cell>
    <Cell ss:StyleID="s75"><Data ss:Type="String">${device.deviceNo}Data>Cell>
    <Cell ss:StyleID="s76"><Data ss:Type="String">${device.remark2}Data>Cell>
Row>
#if>
<#if (device_index > 0)>
<Row ss:AutoFitHeight="0">
    <Cell ss:Index="4" ss:StyleID="s75"><Data ss:Type="String">${device.deviceNo}Data>Cell>
    <Cell ss:StyleID="s76"><Data ss:Type="String">${device.remark2}Data>Cell>
Row>
#if>
#list>
#list>

语法:

<#list itemList as item>

集合长度:${itemList?size}

循环时的下标:item_index

合并单元格:ss:MergeDown="${product.jssDeviceList?size-1}"

4.页面 .html

连续发送多个文件下载请求时,先发起的请求会被自动取消掉,只能成功下载最后一个请求的文件。所以创建多个临时iframe进行请求。

// 打印按钮按下事件
$("#btnPrint").click(function(){
   $("#dataGrid input:checkbox:checked").each(function(){
      var id = $(this).attr("id").substr(13);
      var iframe = document.createElement("iframe");
      iframe.style.display = "none";
      iframe.style.height = 0;
      iframe.src = "${ctx}/jss/jssContract/exportData?id=" + id;
      document.body.appendChild(iframe);
      // 5分钟之后删除
      setTimeout(()=>{
         iframe.remove();
      }, 5 * 60 * 1000);
   });
});
5.Controller
/**
		 * 导出数据到Excel/Word文件
		 * @param request
		 * @param response
		 * @return
		 * @throws IOException
		 */
		@RequestMapping(value = "exportData")
		public void exportData(String id , HttpServletRequest request, HttpServletResponse response) throws IOException, TemplateException
		{
			//获取数据
			JssContract jssContract = jssContractService.getExportData(id);
			Map<String,Object> root = new HashMap<String,Object>();
			root.put("contract", jssContract);
            // Excel
			 response.setHeader("content-Type", "application/msexcel");
            // word
            // response.setHeader("content-Type", "application/msword");
			// 下载文件的默认名称 (word时后缀名改为.doc)
			response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("合同_" + jssContract.getContractNo() + ".xls", "UTF-8"));
			FreemarkerUtil freemarkerUtil = new FreemarkerUtil();
            // 配置的template文件夹下
			Template template = freemarkerUtil.getTemplate("jssContractTemplate.ftl");
			template.process(root,new OutputStreamWriter(response.getOutputStream()));
		}
关于freemarker的语法
1.空值处理的方法

默认情况下,freemarker的变量必须有值,如果没有被赋值的变量在页面上使用就会抛出异常,出错的信息都会显示在页面上。
解决办法:

方法一、我们可以在页面上使用freemarker变量时 以 ${xxx?if_exists} 来处理空值的情况,或采用默认值的方法避免此类问题。但每个freemarker变量都这样处理确实比较让人心烦,请看以下方法。

方法二、在类路径下 加入 freemarker.properties 文件,里面配置 classic_compatible=true。

方法三、通过freemarker.template.Configuration的 config.setClassicCompatible(true);

(通过源码我们看到,其实)方法二、方法三是思想是一致的,只是实现方法不同而已。如果应用中已经存在了 freemarker.properties 并配置了其他的属性,可以在这里配置,否则推荐使用方法三.)

方法四、在ftl文件内引入 。

总结:方法四是需要在每个需要这样处理的页面都要引入的,比较麻烦,、还是选择使用 方法二、方法三好了。但是方法二、方法三也不是万能的。例如我在action中定义一个MyBean类的对象为 myBean,MyBean中有Comp属性。在页面上就要 用如下语句使用: m y B e a n . c o m p , 这 里 m y B e a n 可 能 为 n u l l , c o m p 也 可 能 为 n u l l 。 这 时 候 就 要 使 用 方 法 一 了 , {myBean.comp},这里myBean可能为null,comp也可能为null。这时候 就要使用方法一了, myBean.compmyBeannullcompnull使{(myBean.comp)!} 或 ${(myBean.comp)?if_exists}。

2.部分语法
${book.name?if_exists } //用于判断如果存在,就输出这个值 
${book.name?default(‘xxx’)}//默认值xxx 
${book.name!"xxx"}//默认值xxx 
${book.date?string('yyyy-MM-dd')} //日期格式 
${book?string.number} 20 //三种不同的数字格式 
${book?string.currency}--<#-- $20.00 --> 
${book?string.percent}—<#-- 20% -->

<#assign foo=ture /> //声明变量,插入布尔值进行显示 
${foo?string("yes","no")} <#-- yes -->

大小比较符号使用需要注意:(xml的原因),可以用于比较数字和日期
使用lt、lte、gt和gte来替代<、<=、>和>= 也可以使用括号<#if (x>y)>

1.逻辑判断: if else
<#if condition>... 
<#elseif condition2>... 
<#elseif condition3>...... 
<#else>... 

空值判断:<#if book.name?? >

  1. switch
<#switch value> 
<#case refValue1> 
... 
<#break> 
<#case refValue2> 
... 
<#break> 
<#default> 
... 
#switch>
3:循环读取集合: 注意/的使用
<#list student as stu> 
${stu}<br/>
#list> 

item_index:当前变量的索引值
item_has_next:是否存在下一个对象 其中item名称为as后的变量名,如stu

集合长度判断
<#if student?size != 0> 判断=的时候,注意只要一个=符号,而不是==

4.比较运算符
  1. =或者==:判断两个值是否相等.
  2. !=:判断两个值是否不等.
  3. >或者gt:判断左边值是否大于右边值
  4. >=或者gte:判断左边值是否大于等于右边值
  5. <或者lt:判断左边值是否小于右边值
  6. <=或者lte:判断左边值是否小于等于右边值

5.ftl文本中的空格会被忽略掉,且使用 不能解析,可使用阿斯克码表示空格  

6.多条件关联 且:&& 或:||

你可能感兴趣的:(Java,java,freemarker,excel,msword)