1. 程式人生 > >使用freemarker匯出html格式的word(調整頁邊距,頁面檢視,正常表格樣式)

使用freemarker匯出html格式的word(調整頁邊距,頁面檢視,正常表格樣式)

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中修改,然後儲存,使用記事本開啟就可以看到對應的樣式,希望對各位小夥伴有所幫助!

-------------------------------------------------華麗的分割線-----------------------------------------------------

(原創文件,轉載請註明出處。)