一、页面静态化------>页面预览
为什么要对页面进行管理?
目的是为了能够进快的上线更新的网页。
为什要使用页面静态化?
为了实现以上功能,会用到freeMark,所以接下来学习freeMark
1、FreeMarker 介绍
是一个模板引擎 , 可以用来生成任何文本文件 (js, css, java , txt , html) ; 常用于生成html , 实现页面静态化 ;
freeMarker的小Demo参考另外一篇文章《FreeMark入门与小测试》请点击此链接
2、页面静态化流程
页面静态化流程如下图:
1、静态化程序(核心程序)首先读取页面获取DataUrl。 (DataUrl是集合cms_page中的一个字段)
2、静态化程序远程请求DataUrl 得到数据模型。
3、获取页面模板。
4、执行页面静态化。
2.1获取页面DataUrl-----2.2根据DataUrl获取数据模型
有一个没有做的功能---->数据配置(图片的添加)
在点击【配置】的时候,添加的图片的地址,存储在集合(表)cms_config中。
3、开发轮播图 DataUrl 接口(通过cms_page中的DataUrl来获取数据模型)
3.1、定义DataUrl接口
package com.xuecheng.api.cms;
import com.xuecheng.framework.domain.cms.CmsConfig;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@Api(value="cms配置管理接口",description = "cms配置管理接口,提供数据模型的管理、查询接口")
public interface CmsConfigControllerApi {
@ApiOperation("根据id查询CMS配置信息")
public CmsConfig getCmsConfig(String id);
}
3.2、DataUrl接口的实现-----controller
package com.xuecheng.manage_cms.controller;
import com.xuecheng.api.cms.CmsConfigControllerApi;
import com.xuecheng.framework.domain.cms.CmsConfig;
import com.xuecheng.manage_cms.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;
@RestController
@RequestMapping("/cms/config")
public class CmsConfigController implements CmsConfigControllerApi {
@Autowired
CmsConfigService cmsConfigService;
@Override
@GetMapping("/getmodel/{id}")
public CmsConfig getCmsConfig(@PathVariable("id") String id) {
CmsConfig cmsConfig = cmsConfigService.getCmsConfig(id);
return cmsConfig;
}
}
3.3、DataUrl接口的实现-----service
package com.xuecheng.manage_cms.service;
import com.xuecheng.framework.domain.cms.CmsConfig;
import com.xuecheng.manage_cms.dao.CmsConfigRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class CmsConfigService {
@Autowired
CmsConfigRepository cmsConfigRepository;
public CmsConfig getCmsConfig(String id) {
Optional<CmsConfig> optional = cmsConfigRepository.findById(id);
if(optional.isPresent()){
CmsConfig cmsConfig = optional.get();
return cmsConfig;
}
return null;
}
}
3.4、DataUrl接口的实现-----dao
package com.xuecheng.manage_cms.dao;
import com.xuecheng.framework.domain.cms.CmsConfig;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface CmsConfigRepository extends MongoRepository<CmsConfig,String> {
}
3.5、用java代码发送http请求,访问restful服务(访问DataUrl地址)
RestTemplate
SpringMVC 中提供的一个可用于发送 HTTP 请求的一个模板对象, 底层是对第三方的HTTP库的封装 , 如 : HttpClient, OkHttp ;
|— —添加依赖
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
|— —在启动类里设置
.....
//将以下的返回值,作为spring容器的Bean。
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
.......
|— —在测试类里测试
@Autowired
RestTemplate restTemplate;
@Test
public void testRestTemplate() {
//getForEntity 其中的get是指get请求,Map是指响应回来的类型。
ResponseEntity<Map> forEntity = restTemplate.getForEntity("http://localhost:31001/cms/config/getmodel/5a791725dd573c3574ee333f", Map.class);
System.out.println(forEntity);
}
|— —在测试结果---->可以访问restful风格的http请求,获得正确的结果。
4、模板管理(静态化页面的模块是怎么来的?)
4.1、模板管理流程
4.2、制作模板
模板四要素:注释,插值,指令,文本。
根据html文件,来编写模板文件(.ftl)
<#if model??>
<#list model as item>
<div class="item" style="background-image: url(${item.value});"></div>
</#list>
</#if>
GridFS 概述
介绍 : 是MongoDB中提供的存储文件的系统, 在存储大文件时, 会对文件进行分块存储(256k) ;
在本项目中的关于GridFS说明
两个集合 :
fs.files ----> 存放文件的基本信息(文件名, 文件别名, 文件大小 , md5)
fs.chunks —> 存放文件的内容信息 ;
GridFS 存取文件测试
GridFS 存文件
在GridFS存储文件后,要更新cms_template中的字段 templateFileId
package com.xuecheng.manage_cms.dao.gridfs;
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;
/**
* 测试用GridFS-----存储文件
*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestGridFS {
//注入 GridFsTemplate 类
@Autowired
GridFsTemplate gridFsTemplate;
/**
* 用GridFS------存储文件
*
* 存储原理说明:
* 文件存储成功得到一个文件id 此文件id是fs.files集合中的主键。
* 可以通过文件id查询fs.chunks表中的记录,得到文件的内容。
*/
@Test
public void testGridFs() throws FileNotFoundException {
//要存储的文件
File file = new File("d:/index_banner.ftl");
//定义输入流
FileInputStream inputStram = new FileInputStream(file);
//向GridFS存储文件
ObjectId objectId = gridFsTemplate.store(inputStram, "轮播图测试文件01", "");
//得到文件ID
String fileId = objectId.toString();
System.out.println(fileId);
}
}
index_banner.ftl
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="http://caojiawan.ltd/plugins/normalize-css/normalize.css" />
<link rel="stylesheet" href="http://caojiawan.ltd/plugins/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="http://caojiawan.ltd/css/page-learing-index.css" />
<link rel="stylesheet" href="http://caojiawan.ltd/css/page-header.css" />
</head>
<body>
<div class="banner-roll">
<div class="banner-item">
<#if model??>
<#list model as item>
<div class="item" style="background-image: url(${item.value});"></div>
</#list>
</#if>
<#--<div class="item" style="background-image: url(http://caojiawan.ltd/img/widget-bannerA.jpg);"></div>-->
<#--<div class="item" style="background-image: url(http://caojiawan.ltd/img/widget-banner3.png);"></div>-->
<#--<div class="item" style="background-image: url(http://caojiawan.ltd/img/widget-bannerB.jpg);"></div>-->
<#--<div class="item" style="background-image: url(http://caojiawan.ltd/img/widget-bannerA.jpg);"></div>-->
<#--<div class="item" style="background-image: url(http://caojiawan.ltd/img/widget-banner3.png);"></div>-->
<#--</div>-->
<div class="indicators"></div>
</div>
<script type="text/javascript" src="http://caojiawan.ltd/plugins/jquery/dist/jquery.js"></script>
<script type="text/javascript" src="http://caojiawan.ltd/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>
GridFS 取文件
在config包中定义Mongodb的配置类,如下: GridFSBucket用于打开下载流对象
package com.xuecheng.manage_cms.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;
@Configuration
public class MongoConfig {
//此格式是将yml格式的配置文件内容,注入 String db 中。
@Value("${spring.data.mongodb.database}")
String db;
@Bean
public GridFSBucket getGridFSBucket(MongoClient mongoClient){
MongoDatabase database = mongoClient.getDatabase(db);
GridFSBucket bucket = GridFSBuckets.create(database);
return bucket;
}
}
application.yml
server:
port: 31001
spring:
application:
name: xc‐service‐manage‐cms
data:
mongodb:
uri: mongodb://root:root@localhost:27017
database: xc_cms
取文件测试代码
package com.xuecheng.manage_cms.dao.gridfs;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import org.apache.commons.io.IOUtils;
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.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
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;
import java.io.IOException;
/**
* 测试用GridFS-----存储文件
*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestGridFS {
//注入 GridFsTemplate 类
@Autowired
GridFsTemplate gridFsTemplate;
/**
* 用GridFS------存储文件
*
* 存储原理说明:
* 文件存储成功得到一个文件id 此文件id是fs.files集合中的主键。
* 可以通过文件id查询fs.chunks表中的记录,得到文件的内容。
*/
@Test
public void testGridFs() throws FileNotFoundException {
//要存储的文件
File file = new File("d:/index_banner.ftl");
//定义输入流
FileInputStream inputStram = new FileInputStream(file);
//向GridFS存储文件
ObjectId objectId = gridFsTemplate.store(inputStram, "轮播图测试文件01", "");
//得到文件ID
String fileId = objectId.toString();
System.out.println(fileId);
}
@Autowired
GridFSBucket gridFSBucket;
@Test
public void queryFile() throws IOException {
String fileId = "5e9fc6c549797d1dd0b40414";
//根据id查询文件
// GridFS在数据库中,默认使用fs.chunks和fs.files来存储文件。
// 1.fs.files集合存放文件的信息;
// 2.fs.chunks存放文件数据;
GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(fileId)));
//打开下载流对象
GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
//创建gridFsResource,用于获取流对象
GridFsResource gridFsResource = new GridFsResource(gridFSFile,gridFSDownloadStream);
//获取流中的数据
String s = IOUtils.toString(gridFsResource.getInputStream(),"UTF-8");
System.out.println(s);
}
}
删除文件测试代码
.....
//删除文件
@Test
public void testDelFile() throws IOException {
//根据文件id删除fs.files和fs.chunks中的记录
gridFsTemplate.delete(Query.query(Criteria.where("_id").is("5e9fc6c549797d1dd0b40414")));
}
.....
模板存储
根据模板管理的流程,最终将模板信息存储到MongoDB的cms_template中,将模板文件存储到GridFS中。 模板管理功能在课堂中不再讲解,教学中手动向cms_template及GridFS中存储模板,方法如下:
1、添加模板
1)使用GridFS测试代码存储模板文件到GridFS,并得到文件id.
2)向cms_template添加记录。
2、删除模板
1)使用GridFS测试代码根据文件id删除模板文件。
2)根据模板id删除cms_template中的记录。
3、修改模板信息
使用Studio 3T修改cms_template中的记录。
4、修改模板文件
1)通过Studio 3T修改模板文件(此方法限文件小于256K)
可以通过Studio 3T修改模板文件,先找到模板文件,再导入进去:
真正实现页面静态化
package com.xuecheng.manage_cms.service;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.xuecheng.framework.domain.cms.CmsPage;
import com.xuecheng.framework.domain.cms.CmsTemplate;
import com.xuecheng.framework.domain.cms.response.CmsCode;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.manage_cms.dao.CmsPageRepository;
import com.xuecheng.manage_cms.dao.CmsTemplateRepository;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.TemplateException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.client.RestTemplate;
import freemarker.template.Template;
import java.io.IOException;
import java.util.Map;
import java.util.Optional;
@Service
public class PageService {
@Autowired
CmsPageRepository cmsPageRepository;
@Autowired
RestTemplate restTemplate;
@Autowired
CmsTemplateRepository cmsTemplateRepository;
@Autowired
GridFSBucket gridFSBucket;
//注入 GridFsTemplate 类
@Autowired
GridFsTemplate gridFsTemplate;
/**
* 根据ID找页面
*
* @param pageId
* @return
*/
public CmsPage getPageById(String pageId) {
//判断ID是否存在
if (pageId == null) {
ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXTISTS);
}
//判断页面是否存在
Optional<CmsPage> byId = cmsPageRepository.findById(pageId);
if (!(byId.isPresent())) {
ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXTISTS);
}
CmsPage cmsPage = byId.get();
return cmsPage;
}
//页面静态化
public String getPageHtml(String pageId) throws IOException, TemplateException {
CmsPage cmsPage = this.getPageById(pageId);
//------获取数据模型
//根据页面ID得到CmsPage
//在CmsPage中得到DataUrl
//然后根据DataUrl发送客户端请求,获得数据模型。
Map modelByPageId = this.getModel(cmsPage);
if(modelByPageId==null){
//数据模型获取不到
ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAISNULL);
}
//------获取模板
//根据页面ID得到CmsPage
//在CmsPage中得到templateId
//然后根据templateId得到CmsTemplate
//在CmsTemplate中,得到templateFileId
//然后根据templateFileId,获取模板
//执行页面静态化
String templateByPageId = this.getTemplate(cmsPage);
if(templateByPageId==null){
ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
}
//------执行页面静态化
return this.generateHtml(templateByPageId,modelByPageId);
}
//获取数据模型
//根据页面ID得到CmsPage
//在CmsPage中得到DataUrl
//然后根据DataUrl发送客户端请求,获得数据模型。
private Map getModel(CmsPage cmsPage) {
String dataUrl = cmsPage.getDataUrl();
if(StringUtils.isEmpty(dataUrl)){
ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAURLISNULL);
}
ResponseEntity<Map> forEntity = restTemplate.getForEntity(dataUrl, Map.class);
return forEntity.getBody();
}
//获取模板
//根据页面ID得到CmsPage
//在CmsPage中得到templateId
//然后根据templateId得到CmsTemplate
//在CmsTemplate中,得到templateFileId
//然后根据templateFileId,获取模板
private String getTemplate(CmsPage cmsPage) throws IOException {
String templateId = cmsPage.getTemplateId();
if(StringUtils.isEmpty(templateId)){
ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
}
Optional<CmsTemplate> byId = cmsTemplateRepository.findById(templateId);
if (byId.isPresent()) {
CmsTemplate cmsTemplate = byId.get();
//获取模板文件ID
String templateFileId = cmsTemplate.getTemplateFileId();
//然后根据templateFileId,获取模板
//根据id查询文件
// GridFS在数据库中,默认使用fs.chunks和fs.files来存储文件。
// 1.fs.files集合存放文件的信息;
// 2.fs.chunks存放文件数据;
GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(templateFileId)));
//打开下载流对象
GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
//创建gridFsResource,用于获取流对象
GridFsResource gridFsResource = new GridFsResource(gridFSFile,gridFSDownloadStream);
//获取流中的数据
String s = IOUtils.toString(gridFsResource.getInputStream(),"UTF-8");
return s;
}
return null;
}
//执行页面静态化(以模板字符串方式,实现网页静态化)
private String generateHtml(String template,Map model) throws IOException, TemplateException {
//创建配置类
Configuration configuration = new Configuration(Configuration.getVersion());
//模板加载器
StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
stringTemplateLoader.putTemplate("template", template);
configuration.setTemplateLoader(stringTemplateLoader);
//得到模板
Template template1 = configuration.getTemplate("template", "utf‐8");
//静态化
String content = FreeMarkerTemplateUtils.processTemplateIntoString(template1, model);
//静态化内容
System.out.println(content);
return content;
}
}
单元测试
package com.xuecheng.manage_cms.dao;
import com.xuecheng.framework.domain.cms.CmsPage;
import com.xuecheng.manage_cms.service.PageService;
import freemarker.template.TemplateException;
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.domain.*;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* 这是单元测试,在postman或者swagger测试叫接口测试
*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class FreemarkerTest {
@Autowired
PageService pageService;
@Test
public void freemarkerTest() throws IOException, TemplateException {
String pageHtml = pageService.getPageHtml("5a795ac7dd573c04508f3a56");
System.out.println(pageHtml);
}
}
页面预览
在以上操作后,只得到了String类型的HTML内容,如果要 实现页面预览, 就必须将String类型的数据通过流的方式输出到网页。具体操作代码如下:
package com.xuecheng.framework.web;
import org.springframework.web.bind.annotation.ModelAttribute;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Created by mrt on 2018/5/22.
*/
public class BaseController {
protected HttpServletRequest request;
protected HttpServletResponse response;
protected HttpSession session;
@ModelAttribute
public void setReqAndRes(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
this.session = request.getSession();
}
}
package com.xuecheng.manage_cms.controller;
import com.xuecheng.framework.web.BaseController;
import com.xuecheng.manage_cms.service.PageService;
import freemarker.template.TemplateException;
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;
@Controller
public class CmsPagePreviewController extends BaseController {
@Autowired
PageService pageService;
//接收到页面id
@RequestMapping(value="/cms/preview/{pageId}",method = RequestMethod.GET)
public void preview(@PathVariable("pageId")String pageId) throws IOException, TemplateException {
String pageHtml = pageService.getPageHtml(pageId);
ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(pageHtml.getBytes("utf-8"));
}
}