一. 静态化流程
模型数据 + 页面模板 => 页面静态化 => 预览
不同页面的数据模型不同,为其设置一个dataUrl用于请求模型数据,
使用远程调用RestTemplate请求dataUrl获取到模型数据。
获取页面模板,模板文件统一管理并存储在分布式文件存储系统GridFS中。
执行页面静态化,渲染页面。
二. GridFS
2.1 基本概念
MongoDB提供的分布式文件存储系统。
分块存储,按照256KB的大小分割。
chunks存储文件的二进制数据,files存储文件的元数据信息(文件名,块大小,上传时间等)。
读取文件会将块进行组装合并,因此效率并不是很高。
fs.chunks
fs.files
2.2 代码实现
通过调用GridFsTemplate
的store方法存储,并且返回一个文件id。
会将文件的基本信息存储在fs.files
中(文件id作为主键),而通过files_id
关联到fs.chunks
,文件的二进制数据就存储在其中。
上传文件:
@Autowired
private GridFsTemplate gridFsTemplate;
@Test
public void storeToGridFs() {
// 需要存储的文件
File file = new File("E:\\workspace\\index_banner.ftl");
ObjectId objectId = null;
try {
objectId = gridFsTemplate.store(new FileInputStream(file), "banner_test.ftl");
log.info("【上传文件至GirdFS】, 文件id = {}", objectId);
} catch (FileNotFoundException e) {
log.error("【上传文件至GirdFS有误】 : {}", e);
}
}
返回的文件id:
fs.files:
fs.chunks:
读取文件:
需要GridFSBucket
打开下载流,
而GridFSBucket
需要装配到Spring容器中。
@Configuration
public class MongoConfig {
@Value("${spring.data.mongodb.database}")
private String mongoDB;
@Bean
public GridFSBucket getGridFSBucket(MongoClient mongoClient) {
MongoDatabase database = mongoClient.getDatabase(mongoDB);
GridFSBucket gridFSBucket = GridFSBuckets.create(database);
return gridFSBucket;
}
}
下载文件:
@Autowired
private GridFSBucket gridFSBucket;
@Test
public void downloadFromGridFS() {
// 从gridfs中获取文件
// 1. 根据文件id查询文件
GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is("5c4ac26c8d7e7524b4526852")));
// 2. 打开下载流对象, 需要指定文件id
GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
// 3. 创建gridfsResource对象,获取流
GridFsResource gridFsResource = new GridFsResource(gridFSFile, gridFSDownloadStream);
// 4. 从流中获取数据
try {
String data = IOUtils.toString(gridFsResource.getInputStream(), "utf-8");
log.info("【从GridFS获取文件】: {}", data);
} catch (IOException e) {
log.error("【从GridFS获取文件有误】: ", e);
}
}
即可获取文件内容:
三. 页面静态化
(获取页面数据与模板代码略,通过restTemplate请求dataUrl获取模型数据,模板文件从GridFS下载)
/**
* 页面静态化
* @param templateContent 模板
* @param model 模型数据
* @return
*/
private String generateHtml(String templateContent, Map model) {
// 1. 获取配置对象
Configuration configuration = new Configuration(Configuration.getVersion());
// 2. 获取模板加载器, 可使字符串变为模板文件
StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
stringTemplateLoader.putTemplate("template", templateContent);
// 3. 向configuration配置模板加载器
configuration.setTemplateLoader(stringTemplateLoader);
try {
// 4. 获取模板
Template template = configuration.getTemplate("template");
// 5. 执行静态化页面
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
return html;
} catch (IOException | TemplateException e) {
e.printStackTrace();
return null;
}
}
执行静态化后通过HttpServletResponse写出。
四. 页面动静结合的情况
4.1 方案
动态内容较少的情况下:
静态化生成html页面放在Nginx中。
浏览器可通过Nginx访问到页面。
而页面中的动态数据会通过Ajax请求服务端。
4.2 静态页面分析
分析页面的静态信息与动态信息。
通过SSI拆分成多个页面,通过Nginx再合并。
静态资源,将页面通过SSI技术分割成几块:页头、页尾、根据具体业务分割的其它页面等。
滞后显示(统计信息)的数据可异步加载,静态化成json。
实时显示的数据是完全动态的,引入动态js脚本,用来获取数据。
4.3 配置静态资源虚拟主机(跨域)
配置Nginx的配置文件:静态资源的代理路径等。
cors跨域参数:
location /static/plugins/ {
alias E:/xxxx
add_header Access-Control-Allow-Origin http://xxxx.com;
:允许跨域访问的外域地址。
add_header Access-Control-Allow-Credentials true;
:允许客户端携带证书访问。
add_header Access-Control-Allow-Methods GET;
:允许客户端跨域访问的方法。
}
4.4 页面预览实现步骤
-
页面数据模型查询接口。
-
页面模板(freemarker、thymeleaf)
-
模板文件统一管理,模板信息存储至模板表中,而模板文件存储至GridFS(返回一个文件id,将文件id作为字段存储在模板表中)。
-
返回前端页面预览的url。(需要页面id,新增/更新页面返回页面id,拼接页面预览url)
-
访问页面预览的url调用页面静态化接口。
需要html页面才可解析SSI标签,因此需要设置响应头:
response.setHeader("Content-type", "text/html;charset=utf-8");
其中getPageHtml()
方法中有三步骤,上文有提起,就是获取模型数据与页面模板,然后执行页面静态化,就是上文的generateHtml()
/**
* 页面预览
* @param pageId
* @throws IOException
*/
@GetMapping("/{pageId}")
public void previewPage(@PathVariable("pageId") String pageId) throws IOException {
// 页面静态化
String pageHtml = pageService.getPageHtml(pageId);
// response向浏览器输出
ServletOutputStream outputStream = response.getOutputStream();
// 设置html响应头用于SSI解析
response.setHeader("Content-type", "text/html;charset=utf-8");
outputStream.write(pageHtml.getBytes("utf-8"));
}
(这里的response就是HttpServletResponse,因为继承了一个BaseController,里面有HttpServletRequest、HttpServletResponse、HttpSession)
五. 总结
当操作人员点击页面预览时,会访问一个页面预览url,该url后面拼接的是页面的id。
该地址会请求页面预览的接口,即执行页面静态化。
页面静态化前需要编写页面模板,编写获取页面数据模型的接口。
模板+数据模型=>静态化页面
而预览页面的时候一般都还未存储过该页面,无法得到页面id,所以需要新增页面(多次预览即更新页面)返回一个页面id,再根据静态化接口的请求映射路径拼接成预览url。