学成在线 第4天 讲义-页面静态化 页面预
1 页面静态化需求
1、为什么要进行页面管理?
本项目cms系统的功能就是根据运营需要,对门户等子系统的部分页面进行管理,从而实现快速根据用户需求修改 页面内容并上线的需求。
2、如何修改页面的内容?
在开发中修改页面内容是需要人工编写html及JS文件,CMS系统是通过程序自动化的对页面内容进行修改,通过 页面静态化技术生成html页面。
3、如何对页面进行静态化?
一个页面等于模板加数据,在添加页面的时候我们选择了页面的模板。
页面静态化就是将页面模板和数据通过技术手段将二者合二为一,生成一个html网页文件。
4、页面静态化及页面发布流程图如下
2 FreeMarker 研究
3 页面静态化
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、执行页面静态化。
新建一个test-framarker模块进行测试
引入Pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
</dependency>
</dependencies>
引入配置文件
server:
port: 8088 #服务端口
spring:
application:
name: test-freemarker #指定服务名
freemarker:
cache: false #关闭模板缓存,方便测试
settings:
template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试
创建启动类
@SpringBootApplication
public class FreemarkerTestApplication {
public static void main(String[] args) {
SpringApplication.run(FreemarkerTestApplication.class,args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
}
编写Controller:
@RequestMapping("/freemarker")
@Controller
public class FreemarkerController {
@RequestMapping("/test1")
public String test1(Map<String,Object> map){
map.put("name","这个是freemarker得测试内容");
return "test2";
}
}
进行模板内容得测试:
Freemarker得list命令得测试
获取Map中的对应得属性得值得内容
${stuMap['stu1'].name}</br>
${stuMap['stu1'].age}</br>
${stuMap.stu2.age}</br>
通过遍历map里面得key来实现更简便得方法
<#list stuMap?keys as k>
${stuMap[k].name}
${stuMap[k].age}
${stuMap[k].mondy}
</#list>
进行测试:
freemarker中if指令得使用,当学生得金额大于300时候,显示背景色为红色
完成测试:
freemarker中得空值得处理判断
采用双??得形式进行list得非空得判断对属性得非空判断,采用!''缺省值赋值为空得默认判断
关于freemarker得内建函数得实现
定义模板类型,新建模板测试类,利用工具类生成模板文件,来创建一个html得静态化文件
@Test
public void generatorHtml() throws IOException, TemplateException {
//设置配置类
Configuration configuration=new Configuration(Configuration.getVersion());
//设置模板文件获取文件路径
String classpath = this.getClass().getResource("/").getPath();
configuration.setDirectoryForTemplateLoading(new File(classpath+"/templates/"));
//获取模板路径
Template template = configuration.getTemplate("test2.ftl");
//获取的模板的数据
Map map = getMap();
//开始生成模板
String string = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
//获取文件输入出流,写入到磁盘当中
InputStream inputStream = IOUtils.toInputStream(string);
FileOutputStream outputStream=new FileOutputStream(new File("d:/test1.html"));
IOUtils.copy(inputStream,outputStream);
inputStream.close();
outputStream.close();
}
开始渲染出来模板文件的位置
根据字符串生成模板文件
@Test
public void stringToTemplate() throws IOException, TemplateException {
Configuration configuration=new Configuration(Configuration.getVersion());
//定义模板串
String templateString="" +
"<html>\n" +
" <head></head>\n" +
" <body>\n" +
" 名称:${name}\n" +
" </body>\n" +
"</html>";
StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
stringTemplateLoader.putTemplate("template",templateString);
configuration.setTemplateLoader(stringTemplateLoader);
Template template = configuration.getTemplate("template", "utf-8");
Map map = getMap();
//开始生成模板
String string = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
//获取文件输入出流,写入到磁盘当中
InputStream inputStream = IOUtils.toInputStream(string);
FileOutputStream outputStream=new FileOutputStream(new File("d:/tes232t1.html"));
IOUtils.copy(inputStream,outputStream);
inputStream.close();
outputStream.close();
}
3.2 数据模型
3.2.1 轮播图DataUrl接口 3.2.1.1 需求分析
CMS中有轮播图管理、精品课程推荐的功能,以轮播图管理为例说明:轮播图管理是通过可视化的操作界面由管理 员指定轮播图图片地址,最后将轮播图图片地址保存在cms_config集合中,下边是轮播图数据模型:
关于静态页面的静态化,调用CMSConfig接口的步骤
Controller:
@RestController
@RequestMapping("/cms/config")
public class CmsConfigController implements CmsConfigControllerApi {
@Autowired
private PageService pageService;
@Override
@GetMapping("/getmodel/{id}")
public CmsConfig getmodel(@PathVariable("id") String id) {
return pageService.CmsConfigfindById(id);
}
}
Service:
public CmsConfig CmsConfigfindById(String id){
Optional<CmsConfig> cmsConfig = cmsConfigRepository.findById(id);
if (cmsConfig.isPresent()){
CmsConfig cmsConfig1 = cmsConfig.get();
return cmsConfig1;
}
return null;
}
关于轮播图模板的制作,整个模板的制作过程还是比较简单,只要把原网页复制一下。之后把要替换的数据换成framaker的标签就可以了
<#if model??>
<#list model as item>
<div class="item" style="background-image: url(${item.value});"></div>
</#list>
</#if>
Controller:
@RequestMapping("/banner")
public String test1(Map<String,Object> map){
//向数据模型放数据
ResponseEntity<Map> entity = restTemplate.getForEntity("http://localhost:31001/cms/config/getmodel/5a791725dd573c3574ee333f", Map.class);
Map body = entity.getBody();
map.putAll(body);
return "index_banner";
}
完成测试:
使用GridFsTemplate进行文件的存入
//存文件
@Test
public void testStore() throws FileNotFoundException {
//定义file
File file =new File("d:/index_banner.ftl");
//定义fileInputStream
FileInputStream fileInputStream = new FileInputStream(file);
ObjectId objectId = gridFsTemplate.store(fileInputStream, "index_banner.ftl");
System.out.println(objectId);
}
完成测试:
读取文件
//取文件
@Test
public void queryFile() throws IOException {
//根据文件id查询文件
GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is("5e68b1f810b34108bc9f94b3")));
//打开一个下载流对象
GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
//创建GridFsResource对象,获取流
GridFsResource gridFsResource = new GridFsResource(gridFSFile,gridFSDownloadStream);
//从流中取数据
String content = IOUtils.toString(gridFsResource.getInputStream(), "utf-8");
System.out.println(content);
}
配置类报错,这个BUG就是无法读取到GridFsBucket,因为是在测试类做的,且测试类里面的配置文件无法读取到,在复制一个到test文件加下就可以了
成功读取到内容
这个地方有些绕,确实比较繁琐,因为没有模板的管理的操作只有页面管理的操作,所以有时候,往数据库里面的操作,操作的ID对不上回出现空指针异常,先说明一下,主要分为cms_page这个表里面存着的就是整个页面的配置信息,主要又三个关键的地方一个是,template_id这个字段的意思就是说明,这个页面的模板大致是那个模板,类似数据轮播图模板,或者是课程详情模板,接下来就是主键的ID这个就是这条记录的唯一标识,这个不须多解释,接下来的就是dataUrl,这个就比较不好理解了,因为模板ID 又了,主键ID也又了,需要找个dataURL的作用就是用httplClient通过远程调用接口的方法,来动态生成一个模板的页面,因为我这个dataURL后面跟的ID是从cms_config这个表里面的ID,换而言之,就是这个dataURL的作用决定了要生成什么类型的模板,轮播图类型的模板还是课程详情的模板。这三个字段搞明白了之后,就是开始静态化了,因为需求是轮播图需要更换,更换的前提是不能重复的编写代码,这个需要用程序自动来控制,用Freemarker来实现的时候,需要又两个必备条件,一个是模型数据,一个是模板数据,模型数据可以根据cms_page的主键id查询到,然后查询到这个cms_page对象之后,我们可以获取到对应的dataURL,拿到dataURL就可以生成指定的模板了,这个模板就是cms_config这个里面的不是cms_template这个表里,他生成的配置信息是参照的cms_config,接着就会生成对应的模板,对应的模板这个记录就回存在cms_template这个表中,这个cms_template这个表里面也又几个字段需要注意一下templateFiled这个字段注意是fs_files这个表里的主键,拿这个字段是怎么来的呢,这个字段就是要执行要给Freemaker的测试程序,将模板的信息存入到硬盘,然后会将存取的信息写入的Mongdb中,接着就会生成一个GradFs的主键,这个主键就是template模板的外键,然后这个Tempalte的主键就作为了cms_page的外键,这个过程必须的理清楚,如果理不清楚,这个页面就一直生成不成功,还有一点就是cms_confg他着几个页面的IP地址都是192.168.0.104也就是他提前已经配置了FastFds的文件存储系统,如果没有配置这个,图片是显示不出来了,
接下来就是用程序来控制,显示获取数据模型的代码,接着就是获取模板的代码,最好是执行自动绘的页面静态化
//执行静态化程序
public String generateHtml(String templateConten,Map model){
Configuration configuration=new Configuration(Configuration.getVersion());
StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
stringTemplateLoader.putTemplate("template",templateConten);
configuration.setTemplateLoader(stringTemplateLoader);
try {
Template template = configuration.getTemplate("template");
String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
return content;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//执行根据pageId获取模板数据
public String getTemplateByPageId(String pageId){
CmsPage cmsPage = this.findById(pageId);
if (cmsPage==null){
ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
}
String templateId = cmsPage.getTemplateId();
if (StringUtils.isEmpty(templateId)){
ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
}
//开始执行根据Tmeplate查询对应得模板信息
Optional<CmsTemplate> optional = cmsTemplatesRepository.findById(templateId);
if (optional.isPresent()){
CmsTemplate cmsTemplate = optional.get();
String fileId = cmsTemplate.getTemplateFileId();
//从GridFS中取模板文件内容
//根据文件id查询文件
GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(fileId)));
//打开一个下载流对象
GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
//创建GridFsResource对象,获取流
GridFsResource gridFsResource = new GridFsResource(gridFSFile,gridFSDownloadStream);
//从流中取数据
try {
String content = IOUtils.toString(gridFsResource.getInputStream(), "utf-8");
return content;
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
//执行根据PaGeId获取模型数据
public Map getModelByPageId(String pageId){
CmsPage cmsPage = this.findById(pageId);
if (cmsPage==null){
ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
}
String dataUrl = cmsPage.getDataUrl();
if (StringUtils.isEmpty(dataUrl)){
ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAURLISNULL);
}
//开始发送dataUrl的请求
ResponseEntity<Map> entity = restTemplate.getForEntity(dataUrl, Map.class);
Map body = entity.getBody();
return body;
}
最好就是添加页面预览功能及其Nginx进行访问代理