SpringBoot使用Freemarker导出word模板(OpenXML)

1、OpenXML

本文仅限WPS的word,微软的word另存为XML的格式略有不同

word.docx文档另存为xml之后会生成带有OpenXML标签的文档。

1.1、常用标签示意

标签 解释
<w:wordDocument> XML文档开头描述,包括各种命名空间的描述
<o:DocumentProperties>
<o:CustomDocumentProperties>
<w:fonts>
<w:styles>
<w:bgPict>
<w:docPr>
<w:body>
在<w:wordDocument>中,包含的所有文档主体标签
<w:body> 文档体
<w:sect> 在<w:body>中,描述具体文档体
<w:sectPr> 在<w:sect>中,描述文档样式,注:出现多个<w:sect>时可能会导致莫名其妙的分页
<w:t> 表示真正的文本内容<w:t xml:space="preserve">的意思是无内容时空格会被忽略
<w:p> 段落
<w:r>  样式串,指明它包括的文本的显示样式
<w:hdr> 页眉
<w:ftr>  页脚
<w:val > 一个值
<w:rPr> 在<w:r> 中的标签,是r标签内的样式
<w:pPr> 在<w:p>中的标签,是p标签内的样式
<w:b w:val=”on”> 在样式标签内,描述字体问粗体
<w:jc w:val="right"/> 在样式标签内,描述段落对齐方式为右对齐,可选值有右对齐rignt、左对齐left、居中对齐center、两端对齐both
<w:vAlign w:val="center"/> 在样式标签内,描述表格中的单元格上下对齐方式,可选值有:上top、中center、下bottom
<w:sz w:val="40"/> 在样式标签内,描述字号大小,sz表示Non-Complex Script Font Size,简单理解的话,就是单字节字符(如ASCII编码字符等)的大小
<w:szCs w:val="40"/> 字号,szCs表示Complex Script Font Size,可以简单理解为双字节字符(如中日韩文字、阿拉伯文等)的大小
<w:attr> 自定义XML属性
<w:bookmarkStart>
<w:bookmarkEnd>
书签开始、结束
<w:bCs> 复合字体加粗
<w:rFronts> 在样式标签内,描述字体
<w:hint> 在样式标签内的w:rFonts标签内使用:
<w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:cs="宋体" w:hint="fareast"/>
<w:docPr> 描述文档整个的样式
<w:zoom w:percent="100"/> 在<w:docPr>样式标签下表示视图比例100%
<w:view w:val="print"/> 在<w:docPr>样式标签下表示文档视图是"print"
<w:tbl> 表格标签
<w:tblPr> 在<w:tbl>中,表示表格样式标签
<w:tblBorders> 在<w:tblPr>中,表示表格边框样式
<w:tblGrid> 在<w:tbl>中,定义表格列数以
<w:gridCol w:w="715"/> 在<w:tblGrid>中,定义表格每列的宽度
<w:tr> 在<w:tbl>中,表示表格的行
<w:trPr> 在<w:tr>中,表示表格行的样式
<w:tc> 在<w:tr>中,表示表格的某一行的某个单元格
<w:tcPr> 在<w:tc>中,描述单元格样式
<w:gridSpan w:val="2"/> 左右合并单元格
<w:vmerge w:val="restart"/>
<w:vmerge w:val="continue"/>
上下合并单元格,合并的第一个单元格w:val="restart",下面需要合并的单元格都使用continue
<w:br w:type="page"/> 分页符
<w:pict> 图片区域
<w:binData> 在<w:pict>中,图片源(注:base64图片数据不带【data:image/png;base64,】前缀)
<w:binData w:name="wordml://01.png" xml:space="preserve">base64图片数据</w:binData>
<v:shape>

图片引用占位符,引用的是<w:binData>图片(<v:imagedata src="wordml://01.png" 名称是已存在的图片源)

<v:shape id="图片 10" o:spid="_x0000_s1026" o:spt="75" alt="001.png"  style="xxx" filled="f" o:preferrelative="t" stroked="f" coordsize="21600,21600">
    <v:path/>
    <v:fill on="f" focussize="0,0"/>
    <v:stroke on="f"/>
    <v:imagedata src="wordml://01.png" o:title="001.png"/>
    <o:lock v:ext="edit" aspectratio="t"/>
</v:shape>

1.2、文档大略结构

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument
    xmlns:*="****">
    <o:DocumentProperties>
        <!--作者-->
        <o:Author>xxx</o:Author>
        <!--修改者-->
        <o:LastAuthor>xxx</o:LastAuthor>
        <!--创建时间-->
        <o:Created>xxx</o:Created>
        <!--修改时间-->
        <o:LastSaved>xxx</o:LastSaved>
        <!--时长-->
        <o:TotalTime>0</o:TotalTime>
        <!--页数-->
        <o:Pages>0</o:Pages>
        <!--字数-->
        <o:Words>0</o:Words>
        <!--字节数-->
        <o:Characters>0</o:Characters>
        <!--行数-->
        <o:Lines>0</o:Lines>
        <!--段落数-->
        <o:Paragraphs>0</o:Paragraphs>
        <!--空格数-->
        <o:CharactersWithSpaces>0</o:CharactersWithSpaces>
        <!--版本-->
        <o:Version>14</o:Version>
    </o:DocumentProperties>
    <o:CustomDocumentProperties>
        <!--KSO产品构建版本-->
        <o:KSOProductBuildVer dt:dt="string">xxxx</o:KSOProductBuildVer>
        <o:ICV dt:dt="string">xxxx</o:ICV>
    </o:CustomDocumentProperties>
    <!--字体组-->
    <w:fonts>
        <w:defaultFonts w:ascii="Calibri" w:fareast="宋体" w:h-ansi="Calibri" w:cs="Times New Roman"/>
        <w:font w:name="宋体">
            <w:panose-1 w:val="02010600030101010101"/>
            <w:charset w:val="86"/>
            <w:family w:val="Auto"/>
            <w:pitch w:val="Default"/>
            <w:sig w:usb-0="00000203" w:usb-1="288F0000" w:usb-2="00000006" w:usb-3="00000000" w:csb-0="00040001" w:csb-1="00000000"/>
        </w:font>
    </w:fonts>
    <!--样式组-->
    <w:styles>
        <w:latentStyles w:defLockedState="off" w:latentStyleCount="260">
            <w:lsdException w:name="Normal"/>
        </w:latentStyles>
        <w:style w:type="paragraph" w:styleId="a1" w:default="on">
            <w:name w:val="Normal"/>
            <w:pPr>
                <w:widowControl w:val="off"/>
            </w:pPr>
            <w:rPr>
                <w:rFonts w:ascii="Calibri" w:h-ansi="Calibri" w:fareast="宋体" w:cs="Times New Roman" w:hint="default"/>
                <w:sz w:val="22"/>
                <w:sz-cs w:val="22"/>
                <w:lang w:val="EN-US" w:fareast="EN-US" w:bidi="AR-SA"/>
            </w:rPr>
        </w:style>
    </w:styles>
    <!--文档背景描述-->
    <w:bgPict>
        <w:background/>
        <v:background id="_x0000_s1025">
            <v:fill on="f" focussize="0,0"/>
        </v:background>
    </w:bgPict>
    <!--文档样式-->
    <w:docPr>
        <!--视图-->
        <w:view w:val="print"/>
        <!--缩放-->
        <w:zoom w:percent="100"/>
        <!--字符间距-->
        <w:characterSpacingControl w:val="CompressPunctuation"/>
        <!--文档保护-->
        <w:documentProtection w:enforcement="off"/>
        <!--标点符号相关-->
        <w:punctuationKerning/>
        <!--不嵌入系统字体-->
        <w:doNotEmbedSystemFonts/>
        <!--边界不围绕头部-->
        <w:bordersDontSurroundHeader/>
        <!--边界不围绕尾部-->
        <w:bordersDontSurroundFooter/>
        <w:defaultTabStop w:val="420"/>
        <!--绘图网格垂直间距-->
        <w:drawingGridVerticalSpacing w:val="156"/>
        <!--显示水平绘制网格间隔-->
        <w:displayHorizontalDrawingGridEvery w:val="0"/>
        <!--显示垂直绘制网格间隔-->
        <w:displayVerticalDrawingGridEvery w:val="2"/>
        <!--兼容性描述-->
        <w:compat>
            <!--调整表格中的线条高度-->
            <w:adjustLineHeightInTable/>
            <!--URL尾部空间-->
            <w:ulTrailSpace/>
            <!--不展开移位-->
            <w:doNotExpandShiftReturn/>
            <!--平衡单字节双字节宽度-->
            <w:balanceSingleByteDoubleByteWidth/>
            <!--使用EF布局-->
            <w:useFELayout/>
            <w:spaceForUL/>
            <!--带双关语的包装文本-->
            <w:wrapTextWithPunct/>
            <!--表格换行兼容-->
            <w:breakWrappedTables/>
            <!--使用Asian规则-->
            <w:useAsianBreakRules/>
            <!--自动调整-->
            <w:dontGrowAutofit/>
        </w:compat>
    </w:docPr>
    <!--文档内容-->
    <w:body>
        <!--内容主体-->
        <wx:sect>
            <w:p>
                <w:pPr>
                    <w:spacing w:line="360" w:line-rule="exact"/>
                    <w:jc w:val="center"/>
                    <w:rPr>
                        <w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="default"/>
                        <w:b/>
                        <w:sz w:val="28"/>
                        <w:sz-cs w:val="28"/>
                        <w:lang w:fareast="ZH-CN"/>
                    </w:rPr>
                </w:pPr>
                <w:r>
                    <w:rPr>
                        <w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:hint="fareast"/>
                        <w:b/>
                        <w:sz w:val="28"/>
                        <w:sz-cs w:val="28"/>
                        <w:lang w:fareast="ZH-CN"/>
                    </w:rPr>
                    <w:t>xxx</w:t>
                </w:r>
            </w:p>
        </wx:sect>
        <wx:sect>
            <!--内容样式区域-->
            <w:sectPr>
                <w:pgSz w:w="11906" w:h="16838"/>
                <w:pgMar w:top="1440" w:right="1800" w:bottom="1440" w:left="1800" w:header="851" w:footer="992" w:gutter="0"/>
                <w:cols w:space="720"/>
                <w:docGrid w:type="lines" w:line-pitch="312"/>
            </w:sectPr>
        </wx:sect>
    </w:body>
</w:wordDocument>

2、SpringBoot使用FreeMarker模板导出自定义样式的文档

1、新建Word,里面插入个Table

2、另存为xml文件

3、格式化xml文件并重命名为ftl后缀

可以使用在线格式化工具:在线 XML 格式化 | 菜鸟工具 (runoob.com)

4、修改ftl文件

将ftl文件中1,2,3单元格位置变成${param1},${param2},${param3}

 5、java代码

这里使用SpringBoot2.7.4

5.1、添加pom依赖

<!-- freemarker依赖 -->
<dependency>
	<groupId>org.freemarker</groupId>
	<artifactId>freemarker</artifactId>
	<version>2.3.32</version>
</dependency>

5.2、application配置

【application.properties改成application.yml方便一些】

server:
  port: 9090
spring:
  #freemarker配置
  #默认的classpath:/templates/?
  freemarker:
    template-loader-path: /ftl_templates
    #后缀
    suffix: .ftl
    #编码
    charset: utf-8
    #RequestContext
    request-context-attribute: request

5.3、工具类WordUtil

import freemarker.template.Configuration;
import freemarker.template.Template;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import java.io.*;
import java.net.URLEncoder;
import java.util.Map;

public class WordUtil {

    /**
     * 生成word文件
     */
    @SuppressWarnings("unchecked")
    public static void createWord(HttpServletResponse response, Map dataMap, String templateName, String fileName, String fileSuffix){
        File outFile=null;
        Writer out=null;
        InputStream fin=null;
        ServletOutputStream out2=null;
        try {
            //创建配置实例
            Configuration configuration = new Configuration(Configuration.getVersion());
            //设置编码
            configuration.setDefaultEncoding("UTF-8");
            //ftl模板文件 取模板文件存放地址
            configuration.setClassForTemplateLoading(WordUtil.class,"/ftl_templates");
            //获取模板
            Template template = configuration.getTemplate(templateName);
            //创建临时文件
            outFile = File.createTempFile(fileName, fileSuffix);
            //获取临时文件目录方便后面使用
            String tempFilePath = outFile.getAbsolutePath();
            //将模板和数据模型合并生成文件
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),"UTF-8"));
            //生成文件 实际这里已经将文件生成在指定位置
            template.process(dataMap, out);

            //以下操作是将文件下载
            File file = new File(tempFilePath);
            fin = new FileInputStream(file);
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/msword");
            // 设置浏览器以下载的方式,处理该文件名  ps:docx格式office可能存在打不开等问题
            fileName = URLEncoder.encode(fileName+fileSuffix, "utf-8");
            response.setHeader("Content-Disposition","attachment;filename="+fileName);
            out2 = response.getOutputStream();
            byte[] buffer = new byte[512];
            int bytesToRead = -1;
            // 通过循环将读入的Word文件的内容输出到浏览器中
            while ((bytesToRead = fin.read(buffer)) != -1) {
                out2.write(buffer, 0, bytesToRead);
            }
            //关闭流
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (fin != null) {
                    fin.close();
                }
                if (out2 != null) {
                    out2.close();
                }
                if(outFile!=null) {
                    outFile.delete();
                }
            }catch (Exception e){

            }
        }
    }
}

5.4、Controller类

import com.example.ftldemo.utils.WordUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/demo")
public class DemoController {

    @RequestMapping("/export")
    public void exportDemo(HttpServletResponse response){
        /** 用于组装word页面需要的数据 */
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("param1","111");
        dataMap.put("param2","222");
        dataMap.put("param3","333");
        String fileName = "生成Word文档";
        String fileSuffix=".doc";
        /** 生成word  数据包装,模板名,文件生成路径,生成的文件名*/
        WordUtil.createWord(response,dataMap, "导出wordDemo.ftl", fileName, fileSuffix);
    }
}

5.5、运行使用浏览器

访问    localhost:9090/demo/export  可以下载示例word文档

猜你喜欢

转载自blog.csdn.net/JohnGene/article/details/130074210
今日推荐