第三天頁面靜態化和頁面預覽

一、頁面靜態化需求

1.1 爲什麼要進行頁面管理

本項目cms系統的功能就是根據運營需要,對門戶等子系統的部分頁面進行管理,從而實現快速根據用戶需求修改頁面內容並上線的需求。

1.2 如何修改頁面的內容

在開發中修改頁面內容是需要人工編寫html及JS文件,CMS系統是通過程序自動化的對頁面內容進行修改,通過頁面靜態化技術生成html頁面。

1.3 如何對頁面進行靜態化

一個頁面等於模板加數據,在添加頁面的時候我們選擇了頁面的模板。

頁面靜態化就是將頁面模板和數據通過技術手段將二者合二爲一,生成一個html網頁文件

1.4 頁面靜態化及頁面發佈流程如下圖

業務流程如下:

  1. 獲取模板數據
  2. 製作模板
  3. 對頁面進行靜態化
  4. 將靜態化生成的html頁面存放在文件系統中
  5. 將存在在文件系統的html文件發佈到服務器上

二、FreeMaker入門

三、頁面靜態化

3.1 頁面靜態化流程

通過上邊對FreeMarker的研究我們得出:模板+數據模型=輸出,頁面靜態化需要準備數據模型和模板,先知道數據模型的結構纔可以編寫模板,因爲在模板中要引用數據模型中的數據,本節將系統講解CMS頁面數據模型獲取、模板管理及靜態化的過程。

如何獲取頁面的數據模型?

CMS管理了各種頁面,CMS對頁面進行靜態化時需要數據模型,但是CMS並不知道每個頁面的數據模型的具體內容,它只管執行靜態化程序便可對頁面進行靜態化,所以CMS靜態化程序需要通過一種通用的方法來獲取數據模型。

在編輯頁面信息時指定一個DataUrl,此DataUrl便是獲取數據模型的Url,它基於Http方式,CMS對頁面進行靜態化時會從頁面信息中讀取DataUrl,通過Http遠程調用的方法請求DataUrl獲取數據模型。

管理員怎麼知道DataUrl的內容呢?

舉例說明:

此頁面是輪播圖頁面,它的DataUrl由開發輪播圖管理的程序員提供。

此頁面是精品課程推薦頁面,它的DataUrl由精品課程推薦的程序員提供。

此頁面是課程詳情頁面,它的DataUrl由課程管理的程序員提供。

頁面靜態化流程如下圖:

1、靜態化程序首先讀取頁面獲取DataUrl。

2、靜態化程序遠程請求DataUrl得到數據模型。

3、獲取頁面模板。

4、執行頁面靜態化。

3.2 數據模型

3.2.1 輪播圖DataUrl接口

3.2.1.1 需求分析

CMS中有輪播圖管理、精品課程推薦的功能,以輪播圖管理爲例說明:輪播圖管理是通過可視化的操作界面由管理員指定輪播圖圖片地址,最後將輪播圖圖片地址保存在cms_config集合中,下邊是輪播圖數據模型:

針對首頁的輪播圖信息、精品推薦等信息的獲取統一提供一個Url供靜態化程序調用,這樣我們就知道了輪播圖頁面、精品課程推薦頁面的DataUrl,管理在頁面配置中將此Url配置在頁面信息中。

本小節開發一個查詢輪播圖、精品推薦信息的接口,此接口供靜態化程序調用獲取數據模型。

3.2.1.2 接口定義

輪播圖信息、精品推薦等信息存儲在MongoDB的cms_config集合中

cms_config有固定的數據結構,如下:

package com.xuecheng.framework.domain.cms;

import lombok.Data;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.List;


/**
 * @author 98050
 */
@Data
@ToString
@Document(collection = "cms_config")
public class CmsConfig {

    @Id
    private String id;
    private String name;
    private List<CmsConfigModel> model;

}

數據模型內容如下:

package com.xuecheng.framework.domain.cms;

import lombok.Data;
import lombok.ToString;

import java.util.Map;


/**
 * @author 98050
 */
@Data
@ToString
public class CmsConfigModel {
    /**
     * 主鍵
     */
    private String key;
    /**
     * 項目名稱
     */
    private String name;
    /**
     * 項目url
     */
    private String url;
    /**
     * 項目複雜值
     */
    private Map mapValue;
    /**
     * 項目簡單值
     */
    private String value;

}

上邊的模型結構可以對照cms_config中的數據進行分析。

其中,在mapValue 中可以存儲一些複雜的數據模型內容。

根據配置信息Id查詢配置信息,定義接口如下:

package com.xuecheng.api.cms;

import com.xuecheng.framework.domain.cms.CmsConfig;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @Author: 98050
 * @Time: 2019-03-28 16:44
 * @Feature: cms配置管理接口
 */
@RequestMapping("/cms/config")
@Api(value = "cms配置信息管理接口",description = "cms配置信息管理接口,提供對配置信息的CRUD")
public interface CmsConfigControllerApi {

    /**
     * 根據id查詢CMS配置信息
     * @param id
     * @return
     */
    @ApiOperation("根據id查詢CMS配置信息")
    @GetMapping("/getModel/{id}")
    CmsConfig getModel(@PathVariable("id") String id);
}

3.2.1.3 DAO

package com.xuecheng.managecms.dao;

import com.xuecheng.framework.domain.cms.CmsConfig;
import org.springframework.data.mongodb.repository.MongoRepository;

/**
 * @Author: 98050
 * @Time: 2019-03-28 16:46
 * @Feature:
 */
public interface CmsConfigRepository extends MongoRepository<CmsConfig,String> {
}

3.2.1.4 Service

接口:

package com.xuecheng.managecms.service;

import com.xuecheng.framework.domain.cms.CmsConfig;

/**
 * @Author: 98050
 * @Time: 2019-03-28 16:48
 * @Feature:
 */
public interface CmsConfigService {

    /**
     * 根據id查詢配置管理信息
     * @param id
     * @return
     */
    CmsConfig getConfigById(String id);
}

實現:

package com.xuecheng.managecms.service.impl;

import com.xuecheng.framework.domain.cms.CmsConfig;
import com.xuecheng.managecms.dao.CmsConfigRepository;
import com.xuecheng.managecms.service.CmsConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Optional;

/**
 * @Author: 98050
 * @Time: 2019-03-28 16:49
 * @Feature:
 */
@Service
public class CmsConfigServiceImpl implements CmsConfigService {

    @Autowired
    private CmsConfigRepository cmsConfigRepository;

    /**
     * 根據id查詢配置管理信息
     * @param id
     * @return
     */
    @Override
    public CmsConfig getConfigById(String id) {
        Optional<CmsConfig> optional = cmsConfigRepository.findById(id);
        return optional.orElse(null);
    }
}

3.2.1.5 Controller

package com.xuecheng.managecms.controller;

import com.xuecheng.api.cms.CmsConfigControllerApi;
import com.xuecheng.framework.domain.cms.CmsConfig;
import com.xuecheng.managecms.service.CmsConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: 98050
 * @Time: 2019-03-28 16:52
 * @Feature:
 */
@RestController
@RequestMapping("/cms/config")
public class CmsConfigController implements CmsConfigControllerApi {

    @Autowired
    private CmsConfigService cmsConfigService;

    @Override
    @GetMapping("/getModel/{id}")
    public CmsConfig getModel(@PathVariable("id") String id) {
        return cmsConfigService.getConfigById(id);
    }
}

3.2.1.6 測試

接口文檔測試:

結果:

3.2.2 遠程請求接口

SpringMVC提供 RestTemplate請求http接口,RestTemplate的底層可以使用第三方的http客戶端工具實現http 的請求,常用的http客戶端工具有Apache HttpClient、OkHttpClient等,本項目使用OkHttpClient完成http請求,原因也是因爲它的性能比較出衆。

1、添加依賴

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
</dependency>

2、配置RestTemplate

在SpringBoot啓動類中配置 RestTemplate

@Bean
public RestTemplate restTemplate(){
    return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}

3、測試RestTemplate

根據url獲取數據,並且轉爲map格式。

package com.xuecheng.managecms;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;

import java.util.Map;

/**
 * @Author: 98050
 * @Time: 2019-03-28 17:28
 * @Feature:
 */
@SpringBootTest
@RunWith(SpringRunner.class)
public class CmsPageConfigTest {

    @Autowired
    private RestTemplate restTemplate;


    @Test
    public void testRestTemplate(){
        ResponseEntity<Map> entity = this.restTemplate.getForEntity("http://localhost:31001/cms/config/getModel/5a791725dd573c3574ee333f", Map.class);
        System.out.println(entity.getBody());
    }
}

結果:

3.3 模板管理

3.3.1 模板管理業務流程

CMS提供模板管理功能,業務流程如下:

1、要增加新模板首先需要製作模板,模板的內容就是Freemarker ftl模板內容。

2、通過模板管理模塊功能新增模板、修改模板、刪除模板。

3、模板信息存儲在MongoDB數據庫,其中模板信息存儲在cms_template集合中,模板文件存儲在GridFS文件系統中。

cms_template集合:

上邊模板信息中templateFileId是模板文件的ID,此ID對應GridFS文件系統中文件ID。

3.3.2 模板製作

3.3.2.1 編寫模板文件

1、輪播圖頁面原型

在門戶的靜態工程目錄有輪播圖的靜態頁面,路徑是:/include/index_banner.html。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/plugins/normalize-css/normalize.css" />
    <link rel="stylesheet" href="/plugins/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="/css/page-learing-index.css" />
    <link rel="stylesheet" href="/css/page-header.css" />
</head>
<body>
<div class="banner-roll">
    <div class="banner-item">
        <div class="item" style="background-image: url(../img/widget-bannerB.jpg);"></div>
        <div class="item" style="background-image: url(../img/widget-bannerA.jpg);"></div>
        <div class="item" style="background-image: url(../img/widget-banner3.png);"></div>
        <div class="item" style="background-image: url(../img/widget-bannerB.jpg);"></div>
        <div class="item" style="background-image: url(../img/widget-bannerA.jpg);"></div>
        <div class="item" style="background-image: url(../img/widget-banner3.png);"></div>
    </div>
    <div class="indicators"></div>
</div>
<script type="text/javascript" src="/plugins/jquery/dist/jquery.js"></script>
<script type="text/javascript" src="/plugins/bootstrap/dist/js/bootstrap.js"></script>
<script type="text/javascript">
    var tg = $('.banner-item .item');
    var num = 0;
    for (i = 0; i < tg.length; i++) {
        $('.indicators').append('<span></span>');
        $('.indicators').find('span').eq(num).addClass('active');
    }

    function roll() {
        tg.eq(num).animate({
            'opacity': '1',
            'z-index': num
        }, 1000).siblings().animate({
            'opacity': '0',
            'z-index': 0
        }, 1000);
        $('.indicators').find('span').eq(num).addClass('active').siblings().removeClass('active');
        if (num >= tg.length - 1) {
            num = 0;
        } else {
            num++;
        }
    }
    $('.indicators').find('span').click(function() {
        num = $(this).index();
        roll();
    });
    var timer = setInterval(roll, 3000);
    $('.banner-item').mouseover(function() {
        clearInterval(timer)
    });
    $('.banner-item').mouseout(function() {
        timer = setInterval(roll, 3000)
    });
</script>
</body>
</html>

2、數據模型

通過http獲取到的數據模型如下:

下圖數據模型的圖片路徑改成可以瀏覽的正確路徑

{
  "id": "5a791725dd573c3574ee333f",
  "name": "輪播圖",
  "model": [
    {
      "key": "banner1",
      "name": "輪播圖1地址",
      "url": null,
      "mapValue": null,
      "value": "http://192.168.101.64/group1/M00/00/01/wKhlQFp5wnCAG-kAAATMXxpSaMg864.png"
    },
    {
      "key": "banner2",
      "name": "輪播圖2地址",
      "url": null,
      "mapValue": null,
      "value": "http://192.168.101.64/group1/M00/00/01/wKhlQVp5wqyALcrGAAGUeHA3nvU867.jpg"
    },
    {
      "key": "banner3",
      "name": "輪播圖3地址",
      "url": null,
      "mapValue": null,
      "value": "http://192.168.101.64/group1/M00/00/01/wKhlQFp5wtWAWNY2AAIkOHlpWcs395.jpg"
    }
  ]
}

3、編寫模板

在freemarker測試工程中新建模板index_banner.ftl

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="http://www.xuecheng.com/plugins/normalize-css/normalize.css" />
    <link rel="stylesheet" href="http://www.xuecheng.com/plugins/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="http://www.xuecheng.com/css/page-learing-index.css" />
    <link rel="stylesheet" href="http://www.xuecheng.com/css/page-header.css" />
</head>
<body>
<div class="banner-roll">
    <div class="banner-item">
        <#--<div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-bannerB.jpg);"></div>
        <div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-bannerA.jpg);"></div>
        <div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-banner3.png);"></div>
        <div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-bannerB.jpg);"></div>
        <div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-bannerA.jpg);"></div>
        <div class="item" style="background-image: url(http://www.xuecheng.com/img/widget-banner3.png);"></div>-->
        <#if model??>
            <#list model as item>
                 <div class="item" style="background-image: url(${item.value});"></div>
            </#list>
        </#if>
    </div>
    <div class="indicators"></div>
</div>
<script type="text/javascript" src="http://www.xuecheng.com/plugins/jquery/dist/jquery.js"></script>
<script type="text/javascript" src="http://www.xuecheng.com/plugins/bootstrap/dist/js/bootstrap.js"></script>
<script type="text/javascript">
    var tg = $('.banner-item .item');
    var num = 0;
    for (i = 0; i < tg.length; i++) {
        $('.indicators').append('<span></span>');
        $('.indicators').find('span').eq(num).addClass('active');
    }

    function roll() {
        tg.eq(num).animate({
            'opacity': '1',
            'z-index': num
        }, 1000).siblings().animate({
            'opacity': '0',
            'z-index': 0
        }, 1000);
        $('.indicators').find('span').eq(num).addClass('active').siblings().removeClass('active');
        if (num >= tg.length - 1) {
            num = 0;
        } else {
            num++;
        }
    }
    $('.indicators').find('span').click(function() {
        num = $(this).index();
        roll();
    });
    var timer = setInterval(roll, 3000);
    $('.banner-item').mouseover(function() {
        clearInterval(timer)
    });
    $('.banner-item').mouseout(function() {
        timer = setInterval(roll, 3000)
    });
</script>
</body>
</html>

3.3.2.2 模板測試

在freemarker測試工程編寫一個方法測試輪播圖模板,代碼如下:

@Autowired
RestTemplate restTemplate;

@RequestMapping("/banner")
public String index_banner(Map<String,Object> map){
    String dataUrl = "http://localhost:31001/cms/config/getModel/5a791725dd573c3574ee333f";
    ResponseEntity<Map> entity = restTemplate.getForEntity(dataUrl, Map.class);
    Map body = entity.getBody();
    System.out.println(body);
    if (body != null) {
        map.putAll(body);
    }
    return "index_banner";
}

問題:圖片服務器沒有搭建,修改一下模型數據

{ 
    "_id" : ObjectId("5a791725dd573c3574ee333f"), 
    "_class" : "com.xuecheng.framework.domain.cms.CmsConfig", 
    "name" : "輪播圖", 
    "model" : [
        {
            "key" : "banner1", 
            "name" : "輪播圖1地址", 
            "value" : "http://mycsdnblog.work/201919291432-F.png"
        }, 
        {
            "key" : "banner2", 
            "name" : "輪播圖2地址", 
            "value" : "http://mycsdnblog.work/201919291433-e.png"
        }, 
        {
            "key" : "banner3", 
            "name" : "輪播圖3地址", 
            "value" : "http://mycsdnblog.work/201919291433-0.png"
        }
    ]
}

請求:http://localhost:8088/freemarker/banner

結果:

3.3.3 GridFS研究

3.3.3.1 GridFS介紹

GridFS是MongoDB提供的用於持久化存儲文件的模塊,CMS使用MongoDB存儲數據,使用GridFS可以快速集成開發。

它的工作原理是:在GridFS存儲文件是將文件分塊存儲,文件會按照256KB的大小分割成多個塊進行存儲,GridFS使用兩個集合(collection)存儲文件,一個集合是chunks, 用於存儲文件的二進制數據;一個集合是files,用於存儲文件的元數據信息(文件名稱、塊大小、上傳時間等信息)。

從GridFS中讀取文件要對文件的各各塊進行組裝、合併。

3.3.3.2 GridFS存取文件測試

存文件

使用GridFsTemplate存儲文件測試代碼:

package com.xuecheng.managecms;

import org.bson.types.ObjectId;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

/**
 * @Author: 98050
 * @Time: 2019-03-29 15:57
 * @Feature:
 */
@SpringBootTest
@RunWith(SpringRunner.class)
public class GridFsTest {
    @Autowired
    private GridFsTemplate gridFsTemplate;

    @Test
    public void testGridFs() throws FileNotFoundException {
        //1.要存儲的文件
        File file = new File("D:\\test1.html");
        //2.定義輸入流
        FileInputStream inputStream = new FileInputStream(file);
        //3.向GridFS存儲文件
        ObjectId objectId = gridFsTemplate.store(inputStream, "測試文件", "");
        //4.得到文件ID
        String fileId = objectId.toString();
        System.out.println(fileId);
    }
}

存儲原理:

文件存儲成功得到一個文件Id

此文件Id是fs.files集合中的主鍵

可以通過文件id查詢fs.chunks表中的記錄,得到文件的內容。

讀取文件

1)在config包中定義Mongodb的配置類,如下:

GridFSBucket用於打開下載流對象

package com.xuecheng.managecms.config;


import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: 98050
 * @Time: 2019-03-29 16:12
 * @Feature: mongodb配置類
 */
@Configuration
public class MongoConfig {

    @Value("${spring.data.mongodb.database}")
    private String db;

    @Bean
    public GridFSBucket getGridFSBucket(MongoClient mongoClient){
        MongoDatabase database = mongoClient.getDatabase(db);
        GridFSBucket bucket = GridFSBuckets.create(database);
        return bucket;
    }
}

2)測試代碼

@Autowired
private GridFSBucket gridFSBucket;
/**
 * 讀取文件
 * @throws IOException
 */
@Test
public void queryFile() throws IOException {
    String fileId = "";
    //1.根據id查詢文件
    GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(fileId)));
    assert gridFSFile != null;
    //2.打開下載流對象
    GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
    //3.創建gridResource用於獲取流對象
    GridFsResource gridFsResource = new GridFsResource(gridFSFile,gridFSDownloadStream);
    //4.獲取流中的數據
    String s = IOUtils.toString(gridFsResource.getInputStream(),"UTF-8");
    System.out.println(s);
}

刪除文件

    @Test
    public void testDelFile(){
        String id = "";
        //根據文件id刪除fs.files和fs.chunks中的記錄
        gridFsTemplate.delete(Query.query(Criteria.where("_id").is(id)));
    }

3.3.4 模板存儲

根據模板管理的流程,最終將模板信息存儲到MongoDB的cms_template,將模板文件存儲到GridFS中。

模板管理功能在

3.4 靜態化測試

上邊章節完成了數據模型和模板管理的測試,下邊測試整個頁面靜態化的流程,流程如下:

1、填寫頁面DataUrl

在編輯cms頁面信息界面填寫DataUrl,將此字段保存到cms_page集合中。

2、靜態化程序獲取頁面的DataUrl

3、靜態化程序遠程請求DataUrl獲取數據模型。

4、靜態化程序獲取頁面的模板信息

5、執行頁面靜態化

在CmsService中定義頁面靜態化方法,如下:

/**
 * 頁面靜態化
 * @param pageId
 * @return
 */
@Override
public String getPageHtml(String pageId) {
    //1.獲取頁面模型數據
    Map model = getModelByPageId(pageId);
    if (model == null){
        //2.獲取頁面模型爲空的時候拋出異常
        ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAISNULL);
    }
    //3.獲取頁面模板
    String templateContent = getTemplateByPageId(pageId);
    if (StringUtils.isEmpty(templateContent)){
        //4.頁面模板爲空的話就拋出異常
        ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
    }
    //5.執行靜態化
    String html = generateHtml(templateContent,model);
    if (StringUtils.isEmpty(html)){
        ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_HTMLISNULL);
    }
    return html;
}

整體代碼框架如上所示,接下來就完成getModelByPageIdgetTemplateByPageIdgenerateHtml

三個方法。

3.4.1 getModelByPageId

根據pageId查詢靜態化頁面時所需的模型數據

/**
 * 根據頁面Id查詢模型數據
 * @param pageId
 * @return
 */
private Map getModelByPageId(String pageId) {
    //1.查詢頁面信息
    CmsPage cmsPage = this.findById(pageId);
    if (cmsPage == null){
        //2.頁面不存在
        ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
    }
    //3.取出dataUrl
    String dataUrl = cmsPage.getDataUrl();
    if (StringUtils.isEmpty(dataUrl)){
        ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAISNULL);
    }
    ResponseEntity<Map> entity = this.restTemplate.getForEntity(dataUrl, Map.class);
    return entity.getBody();
}

3.4.2 getTemplateByPageId

根據頁面Id查詢模板信息,獲取到模板文件的id後,把模板文件轉換成字符串返回。

/**
 * 根據頁面Id查詢模板
 * @param pageId
 * @return
 */
private String getTemplateByPageId(String pageId) {
    //1.查詢頁面信息
    CmsPage cmsPage = this.findById(pageId);
    if (cmsPage == null){
        //2.頁面不存在
        ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
    }
    //3.取出getTemplateId
    String templateId = cmsPage.getTemplateId();
    if (StringUtils.isEmpty(templateId)){
        ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
    }
    //4.查詢頁面模板
    Optional<CmsTemplate> optional = this.cmsTemplateRepository.findById(templateId);
    if (optional.isPresent()){
        CmsTemplate cmsTemplate = optional.get();
        //5.模板文件Id
        String templateFileId = cmsTemplate.getTemplateFileId();
        //6.根據id查詢文件
        GridFSFile gridFSFile = this.gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(templateFileId)));
        assert gridFSFile != null;
        //7.打開下載流對象
        GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
        //8.創建gridResource用於獲取流對象
        GridFsResource gridFsResource = new GridFsResource(gridFSFile,gridFSDownloadStream);
        //9.獲取流中的數據
        try {
            return IOUtils.toString(gridFsResource.getInputStream(),"UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return null;
}

3.4.3 generateHtml

拿到模板和數據後,就可以進行頁面的靜態化了。

/**
 * 頁面靜態化
 * @param templateContent
 * @param model
 * @return
 */
private String generateHtml(String templateContent, Map model){
    try {
        //1.創建配置類
        Configuration configuration = new Configuration(Configuration.getVersion());
        //2.模板加載器
        StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
        stringTemplateLoader.putTemplate("template", templateContent);
        configuration.setTemplateLoader(stringTemplateLoader);
        //4.得到模板
        Template template = configuration.getTemplate("template", "utf-8");
        //5.靜態化
        String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
        return html;
    }catch (Exception e){
        e.printStackTrace();
    }
    return null;
}

3.4.4 測試

3.4.4.1 新增測試頁面

結果:

3.4.4.2 上傳輪播圖模板

GridFS上傳index_banner.ftl文件:

結果:

3.4.4.3 修改輪播圖模板中的templateFileId

templateFileId字段的值修改爲剛剛上傳的文件id:5c9e0072dff2be4a682f5f47

修改後的結果:

3.4.4.4 頁面靜態化測試

編寫測試類

package com.xuecheng.managecms;

import com.xuecheng.managecms.service.CmsService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * @Author: 98050
 * @Time: 2019-03-29 18:59
 * @Feature: 頁面靜態化測試
 */
@SpringBootTest
@RunWith(SpringRunner.class)
public class PageStaticTest {

    @Autowired
    private CmsService cmsService;


    @Test
    public void pageStatic(){
        cmsService.getPageHtml("5c9df9dadff2be27386be51b");
    }
}

結果:

完成

四、頁面預覽

4.1 頁面預覽開發

4.1.1 需求分析

頁面在發佈前增加預覽的步驟,方便用戶檢查頁面內容是否正確。具體流程如下所示:

1、用戶進入cms前端,點擊“頁面預覽”在瀏覽器請求cms頁面預覽鏈接

2、cms根據頁面id查詢DataUrl並遠程請求DataUrl獲取數據模型。

3、cms根據頁面id查詢頁面模板內容

4、cms執行頁面靜態化。

5、cms將靜態化內容響應給瀏覽器。

6、在瀏覽器展示頁面內容,實現頁面預覽的功能。

4.1.2 搭建環境

在cms服務中需要集成freemarker

1、在cms服務中加入freemarker的依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

2、在application.yml中配置freemarker

server:
  port: 31001
spring:
  application:
    name: xc-service-manage-cms
  data:
    mongodb:
      uri: mongodb://root:root@localhost:27017
      database: xc_cms
  freemarker:
    cache: false #關閉模板緩存,方便測試
    settings:
      template_update_delay: 0 #檢查模板更新延遲時間,設置爲0表示立即檢查,如果時間大於0會有緩存不方便進行模板測試

4.1.3 Service方法

在靜態化測試中已經完成

4.1.4 Controller

直接調用service的靜態化方法即可,將靜態化內容通過response輸出到瀏覽器顯示

創建CmsPagePreviewController類,用於頁面預覽:

請求頁面id,查詢得到頁面的模板信息、數據模型url,根據數據模型和模板生成靜態化內容,並輸出到瀏覽器。

package com.xuecheng.managecms.controller;

import com.xuecheng.framework.web.BaseController;
import com.xuecheng.managecms.service.CmsService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * @Author: 98050
 * @Time: 2019-03-30 19:12
 * @Feature: 頁面預覽
 */
@Controller
public class CmsPagePreviewController extends BaseController {

    @Autowired
    private CmsService cmsService;

    @RequestMapping(value = "/cms/preview/{pageId}",method = RequestMethod.GET)
    public void preview(@PathVariable("pageId") String pageId){
        String pageHtml = this.cmsService.getPageHtml(pageId);
        if (StringUtils.isNotEmpty(pageHtml)){
            try {
                ServletOutputStream outputStream = response.getOutputStream();
                outputStream.write(pageHtml.getBytes(StandardCharsets.UTF_8));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

測試:http://localhost:31001/cms/preview/5c9df9dadff2be27386be51b

4.2 頁面預覽測試

4.2.1 配置Nginx代理

爲了通過nginx請求靜態資源(css、圖片等),通過nginx代理進行頁面預覽

在 www.xuecheng.com虛擬主機中進行配置:

1553948932834

重啓nginx,測試:

http://www.xuecheng.com/cms/preview/5c9df9dadff2be27386be51b

4.2.2 添加“頁面預覽”連接

添加preview方法:

preview (pageId) {
  window.open('http://www.xuecheng.com/cms/preview/' + pageId)
},

效果:

找到剛剛添加的頁面:

1553951381320

點擊預覽


頁面的靜態化操作放在發佈功能開發中,即靜態頁面上傳到GridFS中,所以對前端頁面進行調整:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章