SpringBoot usa Freemarker para exportar plantillas de Word (OpenXML)

1, XML abierto

Este artículo se limita a WPS Word, y el formato para guardar Microsoft Word como XML es ligeramente diferente

Después de guardar el documento word.docx como xml, se generará un documento con etiquetas OpenXML.

1.1 Etiquetas de uso común

Etiqueta explicar
<w:documentopalabra> Descripción al principio del documento XML, incluidas descripciones de varios espacios de nombres
<o:Propiedades del documento>
<o:Propiedades del documento personalizado>
<w:fuentes>
<w:estilos>
<w:bgPict>
<w:docPr>
<w:cuerpo>
En <w:wordDocument>, todas las etiquetas del cuerpo del documento contenían
<w:cuerpo> cuerpo del documento
<w:secta> En <w:cuerpo>, describa el cuerpo del documento específico
<w:sectPr> En <w:sect>, describa el estilo del documento, nota: cuando hay múltiples <w:sect>s, puede causar saltos de página inexplicables
<w:t> Indica el contenido de texto real <w:t xml:space="preserve"> significa que los espacios se ignorarán cuando no haya contenido
<w:p> párrafo
<w:r>  Una cadena de estilo que especifica el estilo de visualización del texto que contiene
<w:hdr> encabezamiento
<w: pies>  pie de página
<w:valor> un valor
<w:rPr> La etiqueta en <w:r> es el estilo dentro de la etiqueta r
<w:pPr> La etiqueta en <w:p> es el estilo dentro de la etiqueta p
<w:bw:val=”activado”> Dentro de la etiqueta de estilo, describe la fuente en negrita
<w:jc w:val="derecho"/> En la etiqueta de estilo, la alineación del párrafo de descripción es alineación a la derecha, y los valores opcionales son alineación a la derecha, alineación a la izquierda, alineación a la izquierda, alineación al centro, y alineación en ambos extremos.
<w:vAlign w:val="centro"/> En la etiqueta de estilo, describa la alineación superior e inferior de las celdas de la tabla. Los valores opcionales son: superior superior, centro central e inferior inferior
<w:szw:val="40"/> En la etiqueta de estilo, describa el tamaño de fuente, szlo que significa Tamaño de fuente de script no complejo, que es el tamaño de los caracteres de un solo byte (como los caracteres codificados en ASCII, etc.)
<w:szCs w:val="40"/> Tamaño de fuente, szCsque significa Tamaño de fuente de script complejo, que puede entenderse simplemente como el tamaño de los caracteres de doble byte (como chino, japonés, coreano, árabe, etc.)
<w:atributo> Atributos XML personalizados
<w:marcadorInicio>
<w:marcadorFin>
marcador de inicio, final
<w:bCs> Fuente compuesta en negrita
<w:rFrentes> Dentro de la etiqueta de estilo, describe la fuente
<w:pista> Use dentro de la etiqueta w:rFonts dentro de la etiqueta de estilo:
<w:rFonts w:ascii="Arial" w:h-ansi="Arial" w:cs="Arial" w:hint="fareast"/>
<w:docPr> Describe el estilo general del documento.
<w:ampliar w:porcentaje="100"/> Indica que la escala de la vista es del 100% bajo la etiqueta de estilo <w:docPr>
<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文档

Supongo que te gusta

Origin blog.csdn.net/JohnGene/article/details/130074210
Recomendado
Clasificación