Freemarker页面静态化开发

news2024/9/25 9:30:01

4.5 页面静态化

4.5.1 什么是页面静态化

根据课程发布的操作流程,执行课程发布后要将课程详情信息页面静态化,生成html页面上传至文件系统。

什么是页面静态化?

课程预览功能通过模板引擎技术在页面模板中填充数据,生成html页面,这个过程是当客户端请求服务器时服务器才开始渲染生成html页面,最后响应给浏览器,这个过程支持并发是有限的。

页面静态化则强调将生成html页面的过程提前,提前使用模板引擎技术生成html页面,当客户端请求时直接请求html页面,由于是静态页面可以使用nginx、apache等高性能的web服务器,并发性能高。

什么时候能用页面静态化技术?

当数据变化不频繁,一旦生成静态页面很长一段时间内很少变化,此时可以使用页面静态化。因为如果数据变化频繁,一旦改变就需要重新生成静态页面,导致维护静态页面的工作量很大。

根据课程发布的业务需求,虽然课程发布后仍可以修改课程信息,但需要经过课程审核,且修改频度不大,所以适合使用页面静态化。

4.5.2 静态化测试

下边使用freemarker技术对页面静态化生成html页面。

在内容管理service工程中添加freemarker依赖

XML org.springframework.boot spring-boot-starter-freemarker

编写测试方法

Javapackage com.xuecheng.content; import com.xuecheng.content.model.dto.CoursePreviewDto; import com.xuecheng.content.service.CoursePublishService; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; /** * @author Mr.M * @version 1.0 * @description freemarker测试 * @date 2022/9/20 18:42 */ @SpringBootTest public class FreemarkerTest { @Autowired CoursePublishService coursePublishService; //测试页面静态化 @Test public void testGenerateHtmlByTemplate() throws IOException, TemplateException { //配置freemarker Configuration configuration = new Configuration(Configuration.getVersion()); //加载模板 //选指定模板路径,classpath下templates下 //得到classpath路径 String classpath = this.getClass().getResource(“/”).getPath(); configuration.setDirectoryForTemplateLoading(new File(classpath + “/templates/”)); //设置字符编码 configuration.setDefaultEncoding(“utf-8”); //指定模板文件名称 Template template = configuration.getTemplate(“course_template.ftl”); //准备数据 CoursePreviewDto coursePreviewInfo = coursePublishService.getCoursePreviewInfo(2L); Map<String, Object> map = new HashMap<>(); map.put(“model”, coursePreviewInfo); //静态化 //参数1:模板,参数2:数据模型 String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map); System.out.println(content); //将静态化内容输出到文件中 InputStream inputStream = IOUtils.toInputStream(content); //输出流 FileOutputStream outputStream = new FileOutputStream(“D:\develop\test.html”); IOUtils.copy(inputStream, outputStream); } }

执行测试方法,观察D:\develop\test.html 是否成功生成。

4.5.3 上传文件测试

静态化生成文件后需要上传至分布式文件系统,根据微服务的职责划分,媒资管理服务负责维护文件系统中的文件,所以内容管理服务对页面静态化生成html文件需要调用媒资管理服务的上传文件接口。如下图:

微服务之间难免会存在远程调用,在Spring Cloud中可以使用Feign进行远程调用, Feign是什么?

Feign是一个声明式的http客户端,官方地址:https://github.com/OpenFeign/feign

其作用就是帮助我们优雅的实现http请求的发送,解决上面提到的问题。

下边先准备Feign的开发环境,在内容管理content-service工程添加依赖:

XML com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-openfeign io.github.openfeign feign-httpclient io.github.openfeign.form feign-form 3.8.0 io.github.openfeign.form feign-form-spring 3.8.0

在nacos配置feign-dev.yaml公用配置文件

YAMLfeign: client: config: default: # default全局的配置 loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息 httpclient: enabled: true # 开启feign对HttpClient的支持 max-connections: 200 # 最大的连接数 max-connections-per-route: 50 # 每个路径的最大连接数

在内容管理service工程和内容管理api工程都引入此配置文件

YAMLshared-configs: - data-id: feign-${spring.profiles.active}.yaml group: xuecheng-plus-common refresh: true

在内容管理service工程配置feign支持Multipart,拷贝课程资料下的MultipartSupportConfig 到content-service工程下的config包下。

编写feign接口

Javapackage com.xuecheng.content.feignclient; import com.xuecheng.content.config.MultipartSupportConfig; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.multipart.MultipartFile; /** * @description 媒资管理服务远程接口 * @author Mr.M * @date 2022/9/20 20:29 * @version 1.0 */ @FeignClient(value = “media-api”,configuration = MultipartSupportConfig.class) public interface MediaServiceClient { @RequestMapping(value = “/media/upload/coursefile”,consumes = MediaType.MULTIPART_FORM_DATA_VALUE) String uploadFile(@RequestPart(“filedata”) MultipartFile upload,@RequestParam(value = “folder”,required=false) String folder,@RequestParam(value = “objectName”,required=false) String objectName); }

在启动类添加@EnableFeignClients注解

Java@EnableFeignClients(basePackages={“com.xuecheng.content.feignclient”})

编写测试方法

Javapackage com.xuecheng.content; import com.xuecheng.content.feignclient.MediaServiceClient; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.commons.CommonsMultipartFile; import java.io.File; import java.io.FileInputStream; import java.io.OutputStream; /** * @author Mr.M * @version 1.0 * @description 测试使用feign远程上传文件 * @date 2022/9/20 20:36 */ @SpringBootTest public class FeignUploadTest { @Autowired MediaServiceClient mediaServiceClient; //远程调用,上传文件 @Test public void test() { MultipartFile multipartFile = MultipartSupportConfig.getMultipartFile(new File(“D:\develop\test.html”)); mediaServiceClient.uploadFile(multipartFile,“course”,“test.html”); } }

执行测试方法,上传文件成功,进入minIO查看文件

访问:http://192.168.101.65:9000/mediafiles/course/74b386417bb9f3764009dc94068a5e44.html

查看是否可以正常访问。

4.5.4 课程静态化开发

课程页面静态化和静态页面远程上传测试通过,下一步开发课程静态化功能,最终使用消息处理SDK去调度执行。

4.5.4.1 添加消息

课程发布操作使用本地事务保存课程发布信息、添加消息表。

回到当初编写课程发布时的代码,如下:

Java@Transactional @Override public void publish(Long companyId, Long courseId) { //约束校验 //查询课程预发布表 CoursePublishPre coursePublishPre = coursePublishPreMapper.selectById(courseId); if(coursePublishPre == null){ XueChengPlusException.cast(“请先提交课程审核,审核通过才可以发布”); } //本机构只允许提交本机构的课程 if(!coursePublishPre.getCompanyId().equals(companyId)){ XueChengPlusException.cast(“不允许提交其它机构的课程。”); } //课程审核状态 String auditStatus = coursePublishPre.getStatus(); //审核通过方可发布 if(!“202004”.equals(auditStatus)){ XueChengPlusException.cast(“操作失败,课程审核通过方可发布。”); } //保存课程发布信息 saveCoursePublish(courseId); //保存消息表 saveCoursePublishMessage(courseId); //删除课程预发布表对应记录 coursePublishPreMapper.deleteById(courseId); }

我们要填充的saveCoursePublishMessage(courseId)方法,如下:

Java /** * @description 保存消息表记录 * @param courseId 课程id * @return void * @author Mr.M * @date 2022/9/20 16:32 */ private void saveCoursePublishMessage(Long courseId){ MqMessage mqMessage = mqMessageService.addMessage(CoursePublishTask.MESSAGE_TYPE, String.valueOf(courseId), null, null); if(mqMessage!=null){ XueChengPlusException.cast(CommonError.UNKOWN_ERROR); } }

下边进行测试:

发布一门课程,观察消息表是否正常添加消息。

需要手动修改课程审核状态为审核通过执行发布操作,发布后可以修改发布状态为下架重新发布测试。

4.5.4.2 课程静态化实现

课程静态化包括两部分工作:生成课程静态化页面,上传静态页面到文件系统。

在课程发布的service编写这两部分内容,最后通过消息去调度执行。

1、接口定义

Java/** * @description 课程静态化 * @param courseId 课程id * @return File 静态化文件 * @author Mr.M * @date 2022/9/23 16:59 / public File generateCourseHtml(Long courseId); /* * @description 上传课程静态化页面 * @param file 静态化文件 * @return void * @author Mr.M * @date 2022/9/23 16:59 */ public void uploadCourseHtml(Long courseId,File file);

2、接口实现

将之前编写的静态化测试代码以及上传静态文件测试代码拷贝过来使用

Java@Override public File generateCourseHtml(Long courseId) { //静态化文件 File htmlFile = null; try { //配置freemarker Configuration configuration = new Configuration(Configuration.getVersion()); //加载模板 //选指定模板路径,classpath下templates下 //得到classpath路径 String classpath = this.getClass().getResource(“/”).getPath(); configuration.setDirectoryForTemplateLoading(new File(classpath + “/templates/”)); //设置字符编码 configuration.setDefaultEncoding(“utf-8”); //指定模板文件名称 Template template = configuration.getTemplate(“course_template.ftl”); //准备数据 CoursePreviewDto coursePreviewInfo = this.getCoursePreviewInfo(courseId); Map<String, Object> map = new HashMap<>(); map.put(“model”, coursePreviewInfo); //静态化 //参数1:模板,参数2:数据模型 String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map); // System.out.println(content); //将静态化内容输出到文件中 InputStream inputStream = IOUtils.toInputStream(content); //创建静态化文件 htmlFile = File.createTempFile(“course”,“.html”); log.debug(“课程静态化,生成静态文件:{}”,htmlFile.getAbsolutePath()); //输出流 FileOutputStream outputStream = new FileOutputStream(htmlFile); IOUtils.copy(inputStream, outputStream); } catch (Exception e) { e.printStackTrace(); } return htmlFile; } @Override public void uploadCourseHtml(Long courseId, File file) { MultipartFile multipartFile = MultipartSupportConfig.getMultipartFile(file); String course = mediaServiceClient.uploadFile(multipartFile, “course”, courseId+“.html”); }

完善课程发布任务CoursePublishTask类的代码:

Java//生成课程静态化页面并上传至文件系统 public void generateCourseHtml(MqMessage mqMessage,long courseId){ log.debug(“开始进行课程静态化,课程id:{}”,courseId); //消息id Long id = mqMessage.getId(); //消息处理的service MqMessageService mqMessageService = this.getMqMessageService(); //消息幂等性处理 int stageOne = mqMessageService.getStageOne(id); if(stageOne == 1){ log.debug(“课程静态化已处理直接返回,课程id:{}”,courseId); return ; } //生成静态化页面 File file = coursePublishService.generateCourseHtml(courseId); //上传静态化页面 if(file!=null){ coursePublishService.uploadCourseHtml(courseId,file); } //保存第一阶段状态 mqMessageService.completedStageOne(id); }

4.5.4.3 测试

1、启动网关、媒资管理服务工程。

2、在内容管理api工程的启动类上配置FeignClient

Java@EnableFeignClients(basePackages={“com.xuecheng.content.feignclient”})

启动内容管理接口工程。

在CoursePublishTask类的execute方法中打上断点。

3、发布一门课程,保存消息表存在未处理的处理。

4、启动xxl-job调度中心、启动课程发布任务,等待定时调度。

5、观察任务调度日志,观察任务是否可以正常处理。

6、处理完成进入文件系统,查询mediafiles桶内是否存在以课程id命名的html文件

如果不存在说明课程静态化存在问题,再仔细查看执行日志,排查问题。

如果存在,用浏览器访问html文件是否可以正常浏览,下图表示可以正常浏览。

页面还没有样式,需要在nginx配置虚拟目录,在www.xucheng-plus.com下配置:

Plain Text location /course/ { proxy_pass http://fileserver/mediafiles/course/; }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/170764.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mybatis Plus轻松上手

Mybatis Plus 今日目标&#xff1a; 了解mybatisplus的特点能够掌握mybatisplus快速入门能够掌握mybatisplus常用注解能够掌握mybatisplus常用的增删改查能够掌握mybatisplus自动代码生成 Mybatis: ✔需要程序员编写sql语句程序员可以干预sql对sql进行调优(优化) MybatisPlu…

强化学习笔记:基于价值的学习方法之价值估计(python实现)

目录 1. 前言 2. 数学原理 3. 代码实现 3.1 游戏设定 3.2 class State 3.3 class Action 3.4 Class Agent 3.5 Class Environment 4. 仿真结果及其分析 4.1 play() 4.2 value_evaluation_all_states(grid, max_steps) 4.3 value_evaluation_one_state(grid, s) 4.4…

ZYNQ FPGA嵌入式开发 - 小梅哥(二)

创建工程打开Xilinx SDK创建工程Next 创建Empty Application添加文件编写代码参考文档 UG585 Zynq 7000 Technical Reference Manual寄存器说明 Appx.B: Registe Detial查看帮助文档Import Examples跨平台使用&#xff1a;头文件&#xff1a;unistd.h 每个平台都会提供sleep() …

论文阅读笔记:Attention is All You Need

论文标题&#xff1a;Attention is All You Need 目录 论文标题&#xff1a;Attention is All You Need 1.摘要 2.前言 3.模型结构 自注意力机制 多头自注意力机制 注意力机制在Transformer中的应用 1.摘要 过去最优的模型是带有attention连接的encoder-decoder模型&…

string的应用和模拟实现(上)

目录 string的应用 insert插入元素 erase删除元素 assign赋值&#xff1a; replace代替函数的一部分 find&#xff1a;从string对象中找元素 c_str:得到c类型的字符串的指针 substr&#xff1a;取部分元素构建成新的string对象 rfind find_first_of:从string查找元素 stri…

JVM【类的加载过程(类的生命周期)详解】

概述 在 Java 中数据类型分为基本数据类型和引用数据类型。基本数据类型由虚拟机预先定义&#xff0c;引用数据类型则需要进行类的加载。 按照 Java 虚拟机规范&#xff0c;从 class 文件到加载到内存中的类&#xff0c;到类卸载出内存为止&#xff0c;它的整个生命周期包括如…

软件测试之python学习

1、pycharm的常用配置 1.1修改主题配置 1、点击菜单file,选择settings选项2、选择editor&#xff0c;点击color scheme配色方案3、在右侧选择对应的主题配置1.2修改背景颜色 1、点击菜单file,选择settings选项2、选择appearance&#xff0c;点击Theme 1.3调整字体大小 1、点…

基于K8S+eureka的java应用快速上下线的WEB平台

刚进公司时&#xff0c;由于历史原因&#xff0c;应用发布通过&#xff1a;发布新版&#xff08;新老并存&#xff09;->下线老版->删除老版的方式&#xff0c;每次通过手工处理&#xff0c;蛋疼&#xff08;不方便且高风险&#xff09;。于是马上写了比较直观的脚本方案…

关于java移位运算的一点讨论

框架乱飞的年代&#xff0c;时常还得往框架源码里看&#xff0c;对内在原理没点理解&#xff0c;人家就会认为你不太行。平时开发你可能没咋用过位移运算&#xff0c;但往源码里一看&#xff0c;就时常能看到它。我也是看着看着&#xff0c;突然仔细一琢磨&#xff0c;又不由得…

C++缺省参数与函数重载

目录 一.缺省参数 1. 基本概念 2.多参函数中使用缺省参数的情形分类 二.函数重载 (1)形参类型不同构成的重载 (2)形参个数不同构成的重载 (3)形参类型顺序不同构成的重载 函数重载的注意事项&#xff1a; 三.C支持函数重载的底层原理--函数名修饰 编译器生成可执行程序…

选购自主可控全国产交换机时, IP防护等级多少比较合适?

本期武汉海翎光电的小编要为大家介绍的是《选购自主可控全国产交换机时IP防护等级多少比较合适&#xff1f;》首先我们要了解自主可控全国产交换机的工作场景&#xff0c;加固交换机会比工业交换机的IP等级更高一些&#xff0c;而工业交换机又会比普通交换机的IP等级要求高一些…

Unity 工具 之 Jenkins 打包自动化工具的下载/安装/基本操作/任务创建执行/Unity打包自动化简单搭建的相关整理

Unity 工具 之 Jenkins 打包自动化工具的下载/安装/基本操作/任务创建执行/Unity打包自动化简单搭建的相关整理 目录 Unity 工具 之 Jenkins 打包自动化工具的下载/安装/基本操作/任务创建执行/Unity打包自动化简单搭建的相关整理 一、简单介绍 二、Jenkins 的下载 三、Jenk…

代码随想录--链表相关题目整理

代码随想录–链表相关题目整理 1. LeetCode203 移除链表中指定元素 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val…

如何免费创建PDF文档?创建PDF文档的9个工具

PDF 创建器是一种程序、应用程序或软件&#xff0c;旨在制作或创建 PDF 文档。自可移植文档格式 ( PDF ) 出现以来&#xff0c;文档共享和存储变得更加容易。PDF 还使文件交换更加安全。由于 PDF 格式的众多优点&#xff0c;PDF 文档被全球范围内的人们广泛使用。因此&#xff…

Java数据结构(List介绍和顺序表)

1、List的介绍 在集合框架中&#xff0c;List是一个接口&#xff0c;继承自Collection&#xff08;也是一个接口&#xff09;。 Collection也是一个接口&#xff0c;该接口中规范了后序容器中常用的一些方法&#xff0c;Iterable也是一个接口&#xff0c;表示实现该接口的类是可…

第一天总结 之 用户管理界面的实现 之 修改操作 的实现

修改操作 首先 明确 修改操作的前提是 先在页面显示修改前的数据 然后对其进行修改 之后点击提交在页面显示修改前的数据 方法一&#xff1a; 带着数据直接跳转 到添加页面 即在跳转的url后 直接通过&#xff1f;携带数据跳转 缺点&#xff1a; &#xff01;&#xff01;…

6、Ubuntu20的JDKMySQLtomcatRedis安装

安装JDK 这里以安装版本8为例 进入存放jdk目录创建目录 cd /usr/local mkdir jdk cd jdk 把下好的jdk8压缩包拖拽到Ubuntu连接用户下 移动jdk包文件 mv /home/starfish/jdk-8u351-linux-x64.tar.gz . 解压jdk tar -zxvf jdk-8u351-linux-x64.tar.gz cd jdk1.8.0_351/ p…

【C#】C#Process调用外部程序

前言 使用C#调用外部程序&#xff0c;一种是通过Process类&#xff0c;一种是通过命令行&#xff0c;本文主要说一下使用C#中的Process类调用外部程序的方式。 过程&#xff1a; 创建Process对象配置启动选项&#xff08;输入、输出等&#xff09;切换工作目录设置外部程序名…

Java——全排列

题目链接 leetcode在线oj题——全排列 题目描述 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 题目示例 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] …

Linux防火墙状态查看 | 端口关闭 | 端口开启 | 修改命令

目录 firewall防火墙 【1】查看firewall状态 【2】开启、重启、关闭firewalld服务 【3】查看防火墙规则 【3】 添加指定需要开放的端口&#xff08;开启8088&#xff09; 【4】重载入添加的端口 【5】查询指定端口是否开启成功 firewall防火墙 【1】查看firewall状态 s…