使用freemarker导出html格式的word(调整页边距,页面视图,正常表格样式)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011099093/article/details/81010298

RT,耗费了博主半个月的时间才挤出来的成果,在此记录下开发过程。

一、创建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]>
...
<![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中修改,然后保存,使用记事本打开就可以看到对应的样式,希望对各位小伙伴有所帮助!

-------------------------------------------------华丽的分割线-----------------------------------------------------

(原创文档,转载请注明出处。)

猜你喜欢

转载自blog.csdn.net/u011099093/article/details/81010298