怎么样找回网站密码,品牌网站建设小蝌蚪2a,山西防疫最新信息,公司网站做的很烂文章目录 app端文章查看#xff0c;静态化freemarker,分布式文件系统minIO1)文章列表加载1.1)需求分析1.2)表结构分析1.3)导入文章数据库1.3.1)导入数据库1.3.2)导入对应的实体类 1.4)实现思路1.5)接口定义1.6)功能实现1.6.1)#xff1a;导入heima-leadnews-article微服务静态化freemarker,分布式文件系统minIO1)文章列表加载1.1)需求分析1.2)表结构分析1.3)导入文章数据库1.3.1)导入数据库1.3.2)导入对应的实体类 1.4)实现思路1.5)接口定义1.6)功能实现1.6.1)导入heima-leadnews-article微服务资料在当天的文件夹中1.6.2)定义接口1.6.3)编写mapper文件1.6.4)编写业务层代码1.6.5)编写控制器代码1.6.6)swagger测试或前后端联调测试 2)freemarker2.1) freemarker 介绍2.2) 环境搭建快速入门2.2.1) 创建测试工程2.2.2) 配置文件2.2.3) 创建模型类2.2.4) 创建模板2.2.5) 创建controller2.2.6) 创建启动类2.2.7) 测试 2.3) freemarker基础2.3.1) 基础语法种类2.3.2) 集合指令List和Map2.3.3) if指令2.3.4) 运算符2.3.5) 空值处理2.3.6) 内建函数 2.4) 静态化测试2.4.1) 需求分析2.4.2) 静态化测试 3) 对象存储服务MinIO3.1 MinIO简介3.2 MinIO特点3.3 开箱使用3.3.1 安装启动3.3.2 管理控制台 3.4 快速入门3.4.1 创建工程导入pom依赖 3.5 封装MinIO为starter3.5.1 创建模块heima-file-starter3.5.2 配置类3.5.3 封装操作minIO类3.5.4 对外加入自动配置3.5.5 其他微服务使用 4)文章详情4.1)需求分析4.2)实现方案4.3)实现步骤 注 文章信息来源b站黑马程序员相关的教学视频 资料链接b站黑马程序员有资料的获取方式自行get app端文章查看静态化freemarker,分布式文件系统minIO
1)文章列表加载
1.1)需求分析
文章布局展示 1.2)表结构分析
ap_article 文章基本信息表 ap_article_config 文章配置表 ap_article_content 文章内容表 三张表关系分析 1.3)导入文章数据库
1.3.1)导入数据库
查看当天资料文件夹在数据库连接工具中执行leadnews_article.sqlday01中演示过具体步骤
1.3.2)导入对应的实体类
ap_article文章表对应实体
package com.heima.model.article.pojos;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;import java.io.Serializable;
import java.util.Date;/*** p* 文章信息表存储已发布的文章* /p** author itheima*/Data
TableName(ap_article)
public class ApArticle implements Serializable {TableId(value id,type IdType.ID_WORKER)private Long id;/*** 标题*/private String title;/*** 作者id*/TableField(author_id)private Long authorId;/*** 作者名称*/TableField(author_name)private String authorName;/*** 频道id*/TableField(channel_id)private Integer channelId;/*** 频道名称*/TableField(channel_name)private String channelName;/*** 文章布局 0 无图文章 1 单图文章 2 多图文章*/private Short layout;/*** 文章标记 0 普通文章 1 热点文章 2 置顶文章 3 精品文章 4 大V 文章*/private Byte flag;/*** 文章封面图片 多张逗号分隔*/private String images;/*** 标签*/private String labels;/*** 点赞数量*/private Integer likes;/*** 收藏数量*/private Integer collection;/*** 评论数量*/private Integer comment;/*** 阅读数量*/private Integer views;/*** 省市*/TableField(province_id)private Integer provinceId;/*** 市区*/TableField(city_id)private Integer cityId;/*** 区县*/TableField(county_id)private Integer countyId;/*** 创建时间*/TableField(created_time)private Date createdTime;/*** 发布时间*/TableField(publish_time)private Date publishTime;/*** 同步状态*/TableField(sync_status)private Boolean syncStatus;/*** 来源*/private Boolean origin;/*** 静态页面地址*/TableField(static_url)private String staticUrl;
}ap_article_config文章配置对应实体类
package com.heima.model.article.pojos;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;import java.io.Serializable;/*** p* APP已发布文章配置表* /p** author itheima*/Data
TableName(ap_article_config)
public class ApArticleConfig implements Serializable {TableId(value id,type IdType.ID_WORKER)private Long id;/*** 文章id*/TableField(article_id)private Long articleId;/*** 是否可评论* true: 可以评论 1* false: 不可评论 0*/TableField(is_comment)private Boolean isComment;/*** 是否转发* true: 可以转发 1* false: 不可转发 0*/TableField(is_forward)private Boolean isForward;/*** 是否下架* true: 下架 1* false: 没有下架 0*/TableField(is_down)private Boolean isDown;/*** 是否已删除* true: 删除 1* false: 没有删除 0*/TableField(is_delete)private Boolean isDelete;
}ap_article_content 文章内容对应的实体类
package com.heima.model.article.pojos;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;import java.io.Serializable;Data
TableName(ap_article_content)
public class ApArticleContent implements Serializable {TableId(value id,type IdType.ID_WORKER)private Long id;/*** 文章id*/TableField(article_id)private Long articleId;/*** 文章内容*/private String content;
}上述三个java类在资料中给出拷贝这三个文件到article.pojos包中 说明 为什么文章表要拆分成多个表表的拆分-垂直分表 垂直分表:将一个表的字段分散到多个表中每个表存储其中一部分字段。 优势: 1.减少I0争抢减少锁表的几率查看文章概述与文章详情互不影响 2.充分发挥高频数据的操作效率对文章概述数据操作的高效率不会被操作文章详情数据的低效率所拖累。 拆分规则: 1.把不常用的字段单独放在一张表 2.把textblob等大字段拆分出来单独放在一张表 3.经常组合查询的字段单独放在一张表中 1.4)实现思路 1.在默认频道展示10条文章信息
2.可以切换频道查看不同种类文章
3.当用户下拉可以加载最新的文章分页本页文章列表中发布时间为最大的时间为依据
4.当用户上拉可以加载更多的文章信息按照发布时间本页文章列表中发布时间最小的时间为依据
5.如果是当前频道的首页前端传递默认参数
maxBehotTime0毫秒minBehotTime20000000000000毫秒—2063年
1.5)接口定义
加载首页加载更多加载最新接口路径/api/v1/article/load/api/v1/article/loadmore/api/v1/article/loadnew请求方式POSTPOSTPOST参数ArticleHomeDtoArticleHomeDtoArticleHomeDto响应结果ResponseResultResponseResultResponseResult
在dtos包中创建ArticleHomeDto package com.heima.model.article.dtos;import lombok.Data;import java.util.Date;Data
public class ArticleHomeDto {// 最大时间Date maxBehotTime;// 最小时间Date minBehotTime;// 分页sizeInteger size;// 频道IDString tag;
}1.6)功能实现 1.6.1)导入heima-leadnews-article微服务资料在当天的文件夹中 注意需要在heima-leadnews-service的pom文件夹中添加子模块信息如下
modulesmoduleheima-leadnews-user/modulemoduleheima-leadnews-article/module
/modules在idea中的maven中更新一下如果工程还是灰色的需要在重新添加文章微服务的pom文件操作步骤如下 需要在nacos中添加对应的配置
spring:datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/leadnews_article?useUnicodetruecharacterEncodingUTF-8serverTimezoneUTCusername: rootpassword: 13xxxx7
# 设置Mapper接口所对应的XML文件位置如果你在Mapper接口中有自定义方法需要进行该配置
mybatis-plus:mapper-locations: classpath*:mapper/*.xml# 设置别名包扫描路径通过该属性可以给包中的类注册别名type-aliases-package: com.heima.model.article.pojos1.6.2)定义接口 package com.heima.article.controller.v1;import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.common.dtos.ResponseResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;RestController
RequestMapping(/api/v1/article)
public class ArticleHomeController {/*** 加载首页* param dto* return*/PostMapping(/load)public ResponseResult load(RequestBody ArticleHomeDto dto) {return null;}/*** 加载更多* param dto* return*/PostMapping(/loadmore)public ResponseResult loadMore(RequestBody ArticleHomeDto dto) {return null;}/*** 加载最新* param dto* return*/PostMapping(/loadnew)public ResponseResult loadNew(RequestBody ArticleHomeDto dto) {return null;}
}1.6.3)编写mapper文件
package com.heima.article.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.pojos.ApArticle;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;import java.util.List;Mapper
public interface ApArticleMapper extends BaseMapperApArticle {/*** 加载文章列表* param dto* param type 1 加载更多 2 加载最新* return*/public ListApArticle loadArticleList(Param(dto) ArticleHomeDto dto, Param(type) Short type);}对应的映射文件
在resources中新建mapper/ApArticleMapper.xml 如下配置 ?xml version1.0 encodingUTF-8?
!DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.heima.article.mapper.ApArticleMapperresultMap idresultMap typecom.heima.model.article.pojos.ApArticleid columnid propertyid/result columntitle propertytitle/result columnauthor_id propertyauthorId/result columnauthor_name propertyauthorName/result columnchannel_id propertychannelId/result columnchannel_name propertychannelName/result columnlayout propertylayout/result columnflag propertyflag/result columnimages propertyimages/result columnlabels propertylabels/result columnlikes propertylikes/result columncollection propertycollection/result columncomment propertycomment/result columnviews propertyviews/result columnprovince_id propertyprovinceId/result columncity_id propertycityId/result columncounty_id propertycountyId/result columncreated_time propertycreatedTime/result columnpublish_time propertypublishTime/result columnsync_status propertysyncStatus/result columnstatic_url propertystaticUrl//resultMapselect idloadArticleList resultMapresultMapSELECTaa.*FROMap_article aaLEFT JOIN ap_article_config aac ON aa.id aac.article_idwhereand aac.is_delete ! 1and aac.is_down ! 1!-- loadmore --if testtype ! null and type 1and aa.publish_time ![CDATA[]] #{dto.minBehotTime}/ifif testtype ! null and type 2and aa.publish_time ![CDATA[]] #{dto.maxBehotTime}/ifif testdto.tag ! __all__and aa.channel_id #{dto.tag}/if/whereorder by aa.publish_time desclimit #{dto.size}/select/mapper1.6.4)编写业务层代码 package com.heima.article.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.common.dtos.ResponseResult;import java.io.IOException;public interface ApArticleService extends IServiceApArticle {/*** 根据参数加载文章列表* param loadtype 1为加载更多 2为加载最新* param dto* return*/ResponseResult load(Short loadtype, ArticleHomeDto dto);}实现类 package com.heima.article.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.ApArticleService;
import com.heima.common.constants.ArticleConstants;
import com.heima.model.article.dtos.ArticleHomeDto;import com.heima.model.article.pojos.ApArticle;
import com.heima.model.common.dtos.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.Date;
import java.util.List;Service
Transactional
Slf4j
public class ApArticleServiceImpl extends ServiceImplApArticleMapper, ApArticle implements ApArticleService {// 单页最大加载的数字private final static short MAX_PAGE_SIZE 50;Autowiredprivate ApArticleMapper apArticleMapper;/*** 根据参数加载文章列表* param loadtype 1为加载更多 2为加载最新* param dto* return*/Overridepublic ResponseResult load(Short loadtype, ArticleHomeDto dto) {//1.校验参数//分页条数的校验Integer size dto.getSize();if(size null || size 0){size 10;}//分页的值不超过50size Math.min(size,MAX_PAGE_SIZE);dto.setSize(size);//类型参数检验 --loadtypeif(!loadtype.equals(ArticleConstants.LOADTYPE_LOAD_MORE) !loadtype.equals(ArticleConstants.LOADTYPE_LOAD_NEW)){loadtype ArticleConstants.LOADTYPE_LOAD_MORE;}//文章频道校验if(StringUtils.isEmpty(dto.getTag())){dto.setTag(ArticleConstants.DEFAULT_TAG);}//时间校验if(dto.getMaxBehotTime() null) dto.setMaxBehotTime(new Date());if(dto.getMinBehotTime() null) dto.setMinBehotTime(new Date());//2.查询数据ListApArticle apArticles apArticleMapper.loadArticleList(dto, loadtype);//3.结果封装ResponseResult responseResult ResponseResult.okResult(apArticles);return responseResult;}}定义常量类constants package com.heima.common.constants;public class ArticleConstants {public static final Short LOADTYPE_LOAD_MORE 1;public static final Short LOADTYPE_LOAD_NEW 2;public static final String DEFAULT_TAG __all__;}1.6.5)编写控制器代码 package com.heima.article.controller.v1;import com.heima.article.service.ApArticleService;
import com.heima.common.constants.ArticleConstants;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.common.dtos.ResponseResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;RestController
RequestMapping(/api/v1/article)
public class ArticleHomeController {Autowiredprivate ApArticleService apArticleService;PostMapping(/load)public ResponseResult load(RequestBody ArticleHomeDto dto) {return apArticleService.load(ArticleConstants.LOADTYPE_LOAD_MORE,dto);}PostMapping(/loadmore)public ResponseResult loadMore(RequestBody ArticleHomeDto dto) {return apArticleService.load(ArticleConstants.LOADTYPE_LOAD_MORE,dto);}PostMapping(/loadnew)public ResponseResult loadNew(RequestBody ArticleHomeDto dto) {return apArticleService.load(ArticleConstants.LOADTYPE_LOAD_NEW,dto);}
}1.6.6)swagger测试或前后端联调测试
第一在app网关的微服务的nacos的配置中心添加文章微服务的路由完整配置如下
spring:cloud:gateway:globalcors:cors-configurations:[/**]: # 匹配所有请求allowedOrigins: * #跨域处理 允许所有的域allowedMethods: # 支持的方法- GET- POST- PUT- DELETEroutes:# 用户微服务- id: useruri: lb://leadnews-userpredicates:- Path/user/**filters:- StripPrefix 1# 文章微服务- id: articleuri: lb://leadnews-articlepredicates:- Path/article/**filters:- StripPrefix 1第二启动nginx直接使用前端项目测试
第三启动下面三个微服务
文章微服务(ArticleApplication)用户微服务(UserApplication)app网关微服务(AppGatewayApplication)
第四打开网址 http://localhost:8801登录进去会看到文章已经存在可以进行上拉和下拉功能的测试 2)freemarker
文章详情功能的实现 2.1) freemarker 介绍 FreeMarker 是一款 模板引擎 即一种基于模板和要改变的数据 并用来生成输出文本(HTML网页电子邮件配置文件源代码等)的通用工具。 它不是面向最终用户的而是一个Java类库是一款程序员可以嵌入他们所开发产品的组件。
模板编写为FreeMarker Template Language (FTL)。它是简单的专用的语言 不是 像PHP那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示比如数据库查询和业务运算 之后模板显示已经准备好的数据。在模板中你可以专注于如何展现数据 而在模板之外可以专注于要展示什么数据。 常用的java模板引擎还有哪些 Jsp、Freemarker、Thymeleaf 、Velocity 等。
1.Jsp 为 Servlet 专用不能单独进行使用。
2.Thymeleaf 为新技术功能较为强大但是执行的效率比较低。
3.Velocity 从2010年更新完 2.0 版本后便没有在更新。Spring Boot 官方在 1.4 版本后对此也不在支持虽然 Velocity 在 2017 年版本得到迭代但为时已晚。
2.2) 环境搭建快速入门
freemarker作为springmvc一种视图格式默认情况下SpringMVC支持freemarker视图格式。
需要创建Spring BootFreemarker工程用于测试模板。
2.2.1) 创建测试工程
创建一个freemarker-demo 的测试工程专门用于freemarker的功能测试与模板的测试。 pom.xml如下
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdparentartifactIdheima-leadnews-test/artifactIdgroupIdcom.heima/groupIdversion1.0-SNAPSHOT/version/parentmodelVersion4.0.0/modelVersionartifactIdfreemarker-demo/artifactIdpropertiesmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.target/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-freemarker/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactId/dependency!-- lombok --dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/dependency!-- apache 对 java io 的封装工具库 --dependencygroupIdorg.apache.commons/groupIdartifactIdcommons-io/artifactIdversion1.3.2/version/dependency/dependencies/project2.2.2) 配置文件
配置application.yml server:port: 8881 #服务端口
spring:application:name: freemarker-demo #指定服务名freemarker:cache: false #关闭模板缓存方便测试settings:template_update_delay: 0 #检查模板更新延迟时间设置为0表示立即检查如果时间大于0会有缓存不方便进行模板测试suffix: .ftl #指定Freemarker模板文件的后缀名2.2.3) 创建模型类
在freemarker的测试工程下创建模型类型用于测试 package com.heima.freemarker.entity;import lombok.Data;import java.util.Date;Data
public class Student {private String name;//姓名private int age;//年龄private Date birthday;//生日private Float money;//钱包
}2.2.4) 创建模板
在resources下创建templates此目录为freemarker的默认模板存放目录。
在templates下创建模板文件 01-basic.ftl 模板中的插值表达式最终会被freemarker替换成具体的数据。 !DOCTYPE html
html
headmeta charsetutf-8titleHello World!/title
/head
body
b普通文本 String 展示/bbrbr
Hello ${name} br
hr
b对象Student中的数据展示/bbr/
姓名${stu.name}br/
年龄${stu.age}
hr
/body
/html2.2.5) 创建controller
创建Controller类向Map中添加name最后返回模板文件。 package com.xuecheng.test.freemarker.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;import java.util.Map;Controller
public class HelloController {GetMapping(/basic)public String test(Model model) {//1.纯文本形式的参数model.addAttribute(name, freemarker);//2.实体类相关的参数Student student new Student();student.setName(小明);student.setAge(18);model.addAttribute(stu, student);return 01-basic;}
}01-basic.ftl使用插值表达式填充数据
!DOCTYPE html
html
headmeta charsetutf-8titleHello World!/title
/head
body
b普通文本 String 展示/bbrbr
Hello ${name} br
hr
b对象Student中的数据展示/bbr/
姓名${stu.name}br/
年龄${stu.age}
hr
/body
/html2.2.6) 创建启动类 package com.heima.freemarker;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication
public class FreemarkerDemoApplication {public static void main(String[] args) {SpringApplication.run(FreemarkerDemoApplication.class,args);}
}2.2.7) 测试
请求http://localhost:8881/basic 2.3) freemarker基础
2.3.1) 基础语法种类
1、注释即#-- --介于其之间的内容会被freemarker忽略
#--我是一个freemarker注释--2、插值Interpolation即 ${..} 部分freemarker会用真实的值代替 ${..}
Hello ${name}3、FTL指令和HTML标记类似名字前加#予以区分Freemarker会解析标签中的表达式或逻辑。
# FTL指令/# 4、文本仅文本信息这些不是freemarker的注释、插值、FTL指令的内容会被freemarker忽略解析直接输出内容。
#--freemarker中的普通文本--
我是一个普通的文本2.3.2) 集合指令List和Map
1、数据模型
在HelloController中新增如下方法 GetMapping(/list)
public String list(Model model){//------------------------------------Student stu1 new Student();stu1.setName(小强);stu1.setAge(18);stu1.setMoney(1000.86f);stu1.setBirthday(new Date());//小红对象模型数据Student stu2 new Student();stu2.setName(小红);stu2.setMoney(200.1f);stu2.setAge(19);//将两个对象模型数据存放到List集合中ListStudent stus new ArrayList();stus.add(stu1);stus.add(stu2);//向model中存放List集合数据model.addAttribute(stus,stus);//------------------------------------//创建Map数据HashMapString,Student stuMap new HashMap();stuMap.put(stu1,stu1);stuMap.put(stu2,stu2);// 3.1 向model中存放Map数据model.addAttribute(stuMap, stuMap);return 02-list;
}2、模板
在templates中新增02-list.ftl文件 !DOCTYPE html
html
headmeta charsetutf-8titleHello World!/title
/head
body#-- list 数据的展示 --
b展示list中的stu数据:/b
br
br
tabletrtd序号/tdtd姓名/tdtd年龄/tdtd钱包/td/tr
/table
hr#-- Map 数据的展示 --
bmap数据的展示/b
br/br/
a href###方式一通过map[keyname].property/abr/
输出stu1的学生信息br/
姓名br/
年龄br/
br/
a href###方式二通过map.keyname.property/abr/
输出stu2的学生信息br/
姓名br/
年龄br/br/
a href###遍历map中两个学生信息/abr/
tabletrtd序号/tdtd姓名/tdtd年龄/tdtd钱包/td /tr
/table
hr/body
/html实例代码
!DOCTYPE html
html
headmeta charsetutf-8titleHello World!/title
/head
body#-- list 数据的展示 --
b展示list中的stu数据:/b
br
br
tabletrtd序号/tdtd姓名/tdtd年龄/tdtd钱包/td/tr#list stus as stutrtd${stu_index1}/tdtd${stu.name}/tdtd${stu.age}/tdtd${stu.money}/td/tr/#list/table
hr#-- Map 数据的展示 --
bmap数据的展示/b
br/br/
a href###方式一通过map[keyname].property/abr/
输出stu1的学生信息br/
姓名${stuMap[stu1].name}br/
年龄${stuMap[stu1].age}br/
br/
a href###方式二通过map.keyname.property/abr/
输出stu2的学生信息br/
姓名${stuMap.stu2.name}br/
年龄${stuMap.stu2.age}br/br/
a href###遍历map中两个学生信息/abr/
tabletrtd序号/tdtd姓名/tdtd年龄/tdtd钱包/td/tr#list stuMap?keys as key trtd${key_index}/tdtd${stuMap[key].name}/tdtd${stuMap[key].age}/tdtd${stuMap[key].money}/td/tr/#list
/table
hr/body
/html上面代码解释 ${k_index} index得到循环的下标使用方法是在stu后边加_index它的值是从0开始 测试访问 http://localhost:8881/list 2.3.3) if指令 if 指令即判断指令是常用的FTL指令freemarker在解析时遇到if会进行判断条件为真则输出if中间的内容否则跳过内容不再输出。
指令格式
#if /if#if expression
#else
/#if1、数据模型
使用list指令中测试数据模型判断名称为小红的数据字体显示为红色。
2、模板
tabletrtd姓名/tdtd年龄/tdtd钱包/td/tr#list stus as stutrtd ${stu.name}/tdtd${stu.age}/tdtd ${stu.mondy}/td/tr/#list/table实例代码
tabletrtd姓名/tdtd年龄/tdtd钱包/td/tr#list stus as stu #if stu.name小红tr stylecolor: redtd${stu_index}/tdtd${stu.name}/tdtd${stu.age}/tdtd${stu.money}/td/tr#else trtd${stu_index}/tdtd${stu.name}/tdtd${stu.age}/tdtd${stu.money}/td/tr/#if/#list
/table3、输出
姓名为“小红”则字体颜色显示为红色。 2.3.4) 运算符
1、算数运算符
FreeMarker表达式中完全支持算术运算,FreeMarker支持的算术运算符包括:
加法 减法 -乘法 *除法 /求模 (求余) %
模板代码
b算数运算符/b
br/br/1005 运算 ${100 5 }br/100 - 5 * 5运算${100 - 5 * 5}br/5 / 2运算${5 / 2}br/12 % 10运算${12 % 10}br/
hr除了 运算以外其他的运算只能和 number 数字类型的计算。
2、比较运算符
或者:判断两个值是否相等.!:判断两个值是否不等.或者gt:判断左边值是否大于右边值或者gte:判断左边值是否大于等于右边值或者lt:判断左边值是否小于右边值或者lte:判断左边值是否小于等于右边值 和 模板代码
!DOCTYPE html
html
headmeta charsetutf-8titleHello World!/title
/head
bodyb比较运算符/bbr/br/dldt / 和 ! 比较/dtdd#if xiaoming xiaoming字符串的比较 xiaoming xiaoming/#if/dddd#if 10 ! 100数值的比较 10 ! 100/#if/dd/dldldt其他比较/dtdd#if 10 gt 5 形式一使用特殊字符比较数值 10 gt 5/#if/dddd#-- 日期的比较需要通过?date将属性转为data类型才能进行比较 --#if (date1?date date2?date)形式二使用括号形式比较时间 date1?date date2?date/#if/dd/dlbr/
hr
/body
/htmlController 的 数据模型代码
GetMapping(operation)
public String testOperation(Model model) {//构建 Date 数据Date now new Date();model.addAttribute(date1, now);model.addAttribute(date2, now);return 03-operation;
}比较运算符注意 和 ! 可以用于字符串、数值和日期来比较是否相等 和 ! 两边必须是相同类型的值否则会产生错误字符串 x 、x 、X 比较是不等的。因为FreeMarker是精确比较其它的运行符可以作用于数字和日期但不能作用于字符串使用 gt 等字母运算符代替 会有更好的效果因为 FreeMarker会把 解释成FTL标签的结束字符可以使用括号来避免这种情况如:#if (xy)
3、逻辑运算符
逻辑与: 逻辑或: ||逻辑非: !
逻辑运算符只能作用于布尔值,否则将产生错误 。
模板代码
b逻辑运算符/bbr/br/#if (10 lt 12 )( 10 gt 5 ) (10 lt 12 )( 10 gt 5 ) 显示为 true/#ifbr/br/#if !falsefalse 取反为true/#if
hr2.3.5) 空值处理
1、判断某变量是否存在使用 “??”
用法为:variable??如果该变量存在返回true否则返回false
例为防止stus为空报错可以加上判断如下 #if stus??#list stus as stu....../#list/#if2、缺失变量默认值使用 “!” 使用!要以指定一个 默认值当变量为空时显示默认值 例 ${name!}表示如果name为空显示空字符串。 如果是嵌套对象则建议使用括起来 例 ${(stu.bestFriend.name)!}表示如果stu或bestFriend或name为空默认显示空字符串。
2.3.6) 内建函数
内建函数语法格式 变量?函数名称
1、某个集合的大小
${集合名?size}
2、日期格式化
显示年月日: ${today?date} 显示时分秒${today?time} 显示日期时间${today?datetime} 自定义格式化 ${today?string(yyyy年MM月)}
3、内建函数c
model.addAttribute(“point”, 102920122);
point是数字型使用${point}会显示这个数字的值每三位使用逗号分隔。
如果不想显示为每三位分隔的数字可以使用c函数将数字型转成字符串输出
${point?c}
4、将json字符串转成对象
一个例子
其中用到了 assign标签assign的作用是定义一个变量。
#assign text{bank:工商银行,account:10101920201920212} /
#assign datatext?eval /
开户行${data.bank} 账号${data.account}模板代码
!DOCTYPE html
html
headmeta charsetutf-8titleinner Function/title
/head
bodyb获得集合大小/bbr集合大小hrb获得日期/bbr显示年月日: br显示时分秒br显示日期时间br自定义格式化 brhrb内建函数C/bbr没有C函数显示的数值 br有C函数显示的数值hrb声明变量assign/bbrhr
/body
/html内建函数模板页面 在templates创建模版 !DOCTYPE html
html
headmeta charsetutf-8titleinner Function/title
/head
bodyb获得集合大小/bbr集合大小${stus?size}hrb获得日期/bbr显示年月日: ${today?date} br显示时分秒${today?time}br显示日期时间${today?datetime}br自定义格式化 ${today?string(yyyy年MM月)}brhrb内建函数C/bbr没有C函数显示的数值${point} br有C函数显示的数值${point?c}hrb声明变量assign/bbr#assign text{bank:工商银行,account:10101920201920212} /#assign datatext?eval /开户行${data.bank} 账号${data.account}hr
/body
/html内建函数Controller数据模型 在HelloController中添加如下代码 GetMapping(innerFunc)
public String testInnerFunc(Model model) {//1.1 小强对象模型数据Student stu1 new Student();stu1.setName(小强);stu1.setAge(18);stu1.setMoney(1000.86f);stu1.setBirthday(new Date());//1.2 小红对象模型数据Student stu2 new Student();stu2.setName(小红);stu2.setMoney(200.1f);stu2.setAge(19);//1.3 将两个对象模型数据存放到List集合中ListStudent stus new ArrayList();stus.add(stu1);stus.add(stu2);model.addAttribute(stus, stus);// 2.1 添加日期Date date new Date();model.addAttribute(today, date);// 3.1 添加数值model.addAttribute(point, 102920122);return 04-innerFunc;
}测试结果如下
2.4) 静态化测试 之前的测试都是SpringMVC将Freemarker作为视图解析器ViewReporter来集成到项目中工作中有的时候需要使用Freemarker原生Api来生成静态内容下面一起来学习下原生Api生成文本文件。
2.4.1) 需求分析
使用freemarker原生Api将页面生成html文件本节测试html文件生成的方法 2.4.2) 静态化测试
根据模板文件生成html文件
①修改application.yml文件添加以下模板存放位置的配置信息完整配置如下 server:port: 8881 #服务端口
spring:application:name: freemarker-demo #指定服务名freemarker:cache: false #关闭模板缓存方便测试settings:template_update_delay: 0 #检查模板更新延迟时间设置为0表示立即检查如果时间大于0会有缓存不方便进行模板测试suffix: .ftl #指定Freemarker模板文件的后缀名template-loader-path: classpath:/templates #模板存放位置②在test下创建测试类 package com.heima.freemarker.test;import com.heima.freemarker.FreemarkerDemoApplication;
import com.heima.freemarker.entity.Student;
import freemarker.template.Configuration;
import freemarker.template.Template;
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.test.context.junit4.SpringRunner;import java.io.FileWriter;
import java.io.IOException;
import java.util.*;SpringBootTest(classes FreemarkerDemoApplication.class)
RunWith(SpringRunner.class)
public class FreemarkerTest {Autowiredprivate Configuration configuration;Testpublic void test() throws IOException, TemplateException {//freemarker的模板对象获取模板Template template configuration.getTemplate(02-list.ftl);Map params getData();//合成//第一个参数 数据模型//第二个参数 输出流template.process(params, new FileWriter(d:/list.html));}private Map getData() {MapString, Object map new HashMap();//小强对象模型数据Student stu1 new Student();stu1.setName(小强);stu1.setAge(18);stu1.setMoney(1000.86f);stu1.setBirthday(new Date());//小红对象模型数据Student stu2 new Student();stu2.setName(小红);stu2.setMoney(200.1f);stu2.setAge(19);//将两个对象模型数据存放到List集合中ListStudent stus new ArrayList();stus.add(stu1);stus.add(stu2);//向map中存放List集合数据map.put(stus, stus);//创建Map数据HashMapString, Student stuMap new HashMap();stuMap.put(stu1, stu1);stuMap.put(stu2, stu2);//向map中存放Map数据map.put(stuMap, stuMap);//返回Mapreturn map;}
}运行上面代码会在磁盘D中生成一个list.html文件 打开如下
3) 对象存储服务MinIO
3.1 MinIO简介
MinIO基于Apache License v2.0开源协议的对象存储服务可以做为云存储的解决方案用来保存海量的图片视频文档。由于采用Golang实现服务端可以工作在Windows,Linux, OS X和FreeBSD上。配置简单基本是复制可执行程序单行命令可以运行起来。
MinIO兼容亚马逊S3云存储服务接口非常适合于存储大容量非结构化的数据例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等而一个对象文件可以是任意大小从几kb到最大5T不等。
S3 Simple Storage Service简单存储服务
基本概念
bucket – 类比于文件系统的目录Object – 类比文件系统的文件Keys – 类比文件名
官网文档http://docs.minio.org.cn/docs/
3.2 MinIO特点 数据保护 Minio使用Minio Erasure Code纠删码来防止硬件故障。即便损坏一半以上的driver但是仍然可以从中恢复。 高性能 作为高性能对象存储在标准硬件条件下它能达到55GB/s的读、35GB/s的写速率 可扩容 不同MinIO集群可以组成联邦并形成一个全局的命名空间并跨越多个数据中心 SDK支持 基于Minio轻量的特点它得到类似Java、Python或Go等语言的sdk支持 有操作页面 面向用户友好的简单操作界面非常方便的管理Bucket及里面的文件资源 功能简单 这一设计原则让MinIO不容易出错、更快启动 丰富的API 支持文件资源的分享连接及分享链接的过期策略、存储桶操作、文件列表访问及文件上传下载的基本功能等。 文件变化主动通知 存储桶Bucket如果发生改变,比如上传对象和删除对象可以使用存储桶事件通知机制进行监控并通过以下方式发布出去:AMQP、MQTT、Elasticsearch、Redis、NATS、MySQL、Kafka、Webhooks等。
3.3 开箱使用
3.3.1 安装启动
我们提供的镜像中已经有minio的环境
我们可以使用docker进行环境部署和启动
docker run -p 9000:9000 --name minio -d --restartalways -e MINIO_ACCESS_KEYminio -e MINIO_SECRET_KEYminio123 -v /home/data:/data -v /home/config:/root/.minio minio/minio server /data3.3.2 管理控制台
假设我们的服务器地址为http://192.168.200.130:9000我们在地址栏输入http://192.168.200.130:9000/ 即可进入登录界面。 Access Key为minio Secret_key 为minio123 。进入系统后可以看到主界面 点击右下角的“”号 点击下面的图标创建一个桶 3.4 快速入门 3.4.1 创建工程导入pom依赖 创建minio-demo,对应pom如下
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdparentartifactIdheima-leadnews-test/artifactIdgroupIdcom.heima/groupIdversion1.0-SNAPSHOT/version/parentmodelVersion4.0.0/modelVersionartifactIdminio-demo/artifactIdpropertiesmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.target/propertiesdependenciesdependencygroupIdio.minio/groupIdartifactIdminio/artifactIdversion7.1.0/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactId/dependency/dependencies/project引导类 package com.heima.minio;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication
public class MinIOApplication {public static void main(String[] args) {SpringApplication.run(MinIOApplication.class,args);}
}创建测试类上传html文件 package com.heima.minio.test;import io.minio.MinioClient;
import io.minio.PutObjectArgs;import java.io.FileInputStream;public class MinIOTest {/*** 把list.html文件上传到minio中并可以在浏览器中访问* param args*/public static void main(String[] args) {FileInputStream fileInputStream null;try {fileInputStream new FileInputStream(D:\\list.html);;//1.创建minio链接客户端MinioClient minioClient MinioClient.builder().credentials(minio, minio123).endpoint(http://192.168.200.130:9000).build();//2.上传PutObjectArgs putObjectArgs PutObjectArgs.builder().object(list.html)//文件名.contentType(text/html)//文件类型.bucket(leadnews)//桶名词 与minio创建的名词一致.stream(fileInputStream, fileInputStream.available(), -1) //文件流.build();minioClient.putObject(putObjectArgs);System.out.println(http://192.168.200.130:9000/leadnews/list.html);} catch (Exception ex) {ex.printStackTrace();}}}运行上述代码会将list.html上传到minio中 3.5 封装MinIO为starter 注意将moduleheima-leadnews-basic/module加入到父工程heima_leadnews的pom文件中 3.5.1 创建模块heima-file-starter 导入依赖
dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-autoconfigure/artifactId/dependencydependencygroupIdio.minio/groupIdartifactIdminio/artifactIdversion7.1.0/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-configuration-processor/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-actuator/artifactId/dependency
/dependencies在minio-demo的pom文件中加入依赖 dependencygroupIdcom.heima/groupIdartifactIdheima-file-starter/artifactIdversion1.0-SNAPSHOT/version/dependency在微服务中添加minio所需要的配置application.yml minio:accessKey: miniosecretKey: minio123bucket: leadnewsendpoint: http://192.168.200.130:9000readPath: http://192.168.200.130:90003.5.2 配置类
MinIOConfigProperties package com.heima.file.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;import java.io.Serializable;Data
ConfigurationProperties(prefix minio) // 文件上传 配置前缀file.oss
public class MinIOConfigProperties implements Serializable {private String accessKey;private String secretKey;private String bucket;private String endpoint;private String readPath;
}MinIOConfig package com.heima.file.config;import com.heima.file.service.FileStorageService;
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;Data
Configuration
EnableConfigurationProperties({MinIOConfigProperties.class})
//当引入FileStorageService接口时
ConditionalOnClass(FileStorageService.class)
public class MinIOConfig {Autowiredprivate MinIOConfigProperties minIOConfigProperties;Beanpublic MinioClient buildMinioClient(){return MinioClient.builder().credentials(minIOConfigProperties.getAccessKey(), minIOConfigProperties.getSecretKey()).endpoint(minIOConfigProperties.getEndpoint()).build();}
}3.5.3 封装操作minIO类
FileStorageService
package com.heima.file.service;import java.io.InputStream;/*** author itheima*/
public interface FileStorageService {/*** 上传图片文件* param prefix 文件前缀* param filename 文件名* param inputStream 文件流* return 文件全路径*/public String uploadImgFile(String prefix, String filename,InputStream inputStream);/*** 上传html文件* param prefix 文件前缀* param filename 文件名* param inputStream 文件流* return 文件全路径*/public String uploadHtmlFile(String prefix, String filename,InputStream inputStream);/*** 删除文件* param pathUrl 文件全路径*/public void delete(String pathUrl);/*** 下载文件* param pathUrl 文件全路径* return**/public byte[] downLoadFile(String pathUrl);}MinIOFileStorageService package com.heima.file.service.impl;import com.heima.file.config.MinIOConfig;
import com.heima.file.config.MinIOConfigProperties;
import com.heima.file.service.FileStorageService;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Import;
import org.springframework.util.StringUtils;import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;Slf4j
EnableConfigurationProperties(MinIOConfigProperties.class)
Import(MinIOConfig.class)
public class MinIOFileStorageService implements FileStorageService {Autowiredprivate MinioClient minioClient;Autowiredprivate MinIOConfigProperties minIOConfigProperties;private final static String separator /;/*** param dirPath* param filename yyyy/mm/dd/file.jpg* return*/public String builderFilePath(String dirPath,String filename) {StringBuilder stringBuilder new StringBuilder(50);if(!StringUtils.isEmpty(dirPath)){stringBuilder.append(dirPath).append(separator);}SimpleDateFormat sdf new SimpleDateFormat(yyyy/MM/dd);String todayStr sdf.format(new Date());stringBuilder.append(todayStr).append(separator);stringBuilder.append(filename);return stringBuilder.toString();}/*** 上传图片文件* param prefix 文件前缀* param filename 文件名* param inputStream 文件流* return 文件全路径*/Overridepublic String uploadImgFile(String prefix, String filename,InputStream inputStream) {String filePath builderFilePath(prefix, filename);try {PutObjectArgs putObjectArgs PutObjectArgs.builder().object(filePath).contentType(image/jpg).bucket(minIOConfigProperties.getBucket()).stream(inputStream,inputStream.available(),-1).build();minioClient.putObject(putObjectArgs);StringBuilder urlPath new StringBuilder(minIOConfigProperties.getReadPath());urlPath.append(separatorminIOConfigProperties.getBucket());urlPath.append(separator);urlPath.append(filePath);return urlPath.toString();}catch (Exception ex){log.error(minio put file error.,ex);throw new RuntimeException(上传文件失败);}}/*** 上传html文件* param prefix 文件前缀* param filename 文件名* param inputStream 文件流* return 文件全路径*/Overridepublic String uploadHtmlFile(String prefix, String filename,InputStream inputStream) {String filePath builderFilePath(prefix, filename);try {PutObjectArgs putObjectArgs PutObjectArgs.builder().object(filePath).contentType(text/html).bucket(minIOConfigProperties.getBucket()).stream(inputStream,inputStream.available(),-1).build();minioClient.putObject(putObjectArgs);StringBuilder urlPath new StringBuilder(minIOConfigProperties.getReadPath());urlPath.append(separatorminIOConfigProperties.getBucket());urlPath.append(separator);urlPath.append(filePath);return urlPath.toString();}catch (Exception ex){log.error(minio put file error.,ex);ex.printStackTrace();throw new RuntimeException(上传文件失败);}}/*** 删除文件* param pathUrl 文件全路径*/Overridepublic void delete(String pathUrl) {String key pathUrl.replace(minIOConfigProperties.getEndpoint()/,);int index key.indexOf(separator);String bucket key.substring(0,index);String filePath key.substring(index1);// 删除ObjectsRemoveObjectArgs removeObjectArgs RemoveObjectArgs.builder().bucket(bucket).object(filePath).build();try {minioClient.removeObject(removeObjectArgs);} catch (Exception e) {log.error(minio remove file error. pathUrl:{},pathUrl);e.printStackTrace();}}/*** 下载文件* param pathUrl 文件全路径* return 文件流**/Overridepublic byte[] downLoadFile(String pathUrl) {String key pathUrl.replace(minIOConfigProperties.getEndpoint()/,);int index key.indexOf(separator);String bucket key.substring(0,index);String filePath key.substring(index1);InputStream inputStream null;try {inputStream minioClient.getObject(GetObjectArgs.builder().bucket(minIOConfigProperties.getBucket()).object(filePath).build());} catch (Exception e) {log.error(minio down file error. pathUrl:{},pathUrl);e.printStackTrace();}ByteArrayOutputStream byteArrayOutputStream new ByteArrayOutputStream();byte[] buff new byte[100];int rc 0;while (true) {try {if (!((rc inputStream.read(buff, 0, 100)) 0)) break;} catch (IOException e) {e.printStackTrace();}byteArrayOutputStream.write(buff, 0, rc);}return byteArrayOutputStream.toByteArray();}
}3.5.4 对外加入自动配置
在resources中新建META-INF/spring.factories org.springframework.boot.autoconfigure.EnableAutoConfiguration\com.heima.file.service.impl.MinIOFileStorageService3.5.5 其他微服务使用
第一导入heima-file-starter的依赖
第二在微服务中添加minio所需要的配置
minio:accessKey: miniosecretKey: minio123bucket: leadnewsendpoint: http://192.168.200.130:9000readPath: http://192.168.200.130:9000第三在对应使用的业务类中注入FileStorageService样例如下
package com.heima.minio.test;import com.heima.file.service.FileStorageService;
import com.heima.minio.MinIOApplication;
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;import java.io.FileInputStream;
import java.io.FileNotFoundException;SpringBootTest(classes MinIOApplication.class)
RunWith(SpringRunner.class)
public class MinioTest {Autowiredprivate FileStorageService fileStorageService;Testpublic void testUpdateImgFile() {try {FileInputStream fileInputStream new FileInputStream(E:\\tmp\\ak47.jpg);String filePath fileStorageService.uploadImgFile(, ak47.jpg, fileInputStream);System.out.println(filePath);} catch (FileNotFoundException e) {e.printStackTrace();}}
}4)文章详情
4.1)需求分析 4.2)实现方案
方案一
用户某一条文章根据文章的id去查询文章内容表返回渲染页面 方案二采用 4.3)实现步骤
1.在artile微服务中添加MinIO和freemarker的支持参考测试项目
2.资料中找到模板文件article.ftl拷贝到article微服务下 3.资料中找到index.js和index.css两个文件手动上传到MinIO中
将资料中存放index.js和index.css的文件目录拷贝到D:/temp_minio中 修改minio-demo包中的MinIOTest.java package com.heima.minio;import com.heima.file.service.FileStorageService;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
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;import java.io.FileInputStream;
import java.io.FileNotFoundException;SpringBootTest(classes MinIOApplication.class)
RunWith(SpringRunner.class)
public class MinIOTest {Autowiredprivate FileStorageService fileStorageService;//把list.html文件上传到minio中并可以在浏览器中访问// Test
// public void test() throws FileNotFoundException {
// FileInputStream fileInputStream new FileInputStream(D:\\list.html);
// String path fileStorageService.uploadHtmlFile(,list.html,fileInputStream);
// System.out.println(path);
// }/*** 把list.html文件上传到minio中并可以在浏览器中访问* param args*/public static void main(String[] args) {FileInputStream fileInputStream null;try {fileInputStream new FileInputStream(D:\\tmp_minio\\css\\index.css);;//1.创建minio链接客户端MinioClient minioClient MinioClient.builder().credentials(minio, minio123).endpoint(http://192.168.200.130:9000).build();//2.上传PutObjectArgs putObjectArgs PutObjectArgs.builder().object(plugins/css/index.css)//文件名.contentType(text/css)//文件类型.bucket(leadnews)//桶名词 与minio创建的名词一致.stream(fileInputStream, fileInputStream.available(), -1) //文件流.build();minioClient.putObject(putObjectArgs);//访问路径
// System.out.println(http://192.168.200.130:9000/leadnews/list.html);} catch (Exception ex) {ex.printStackTrace();}}}运行public static void main(String[] args)函数会将css\index.css上传到minio中。 同理将js\index.js上传到minio中。
上传成功后打开minio的目录结构如下
4.在文章微服务中导入依赖 dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-freemarker/artifactId/dependencydependencygroupIdcom.heima/groupIdartifactIdheima-file-starter/artifactIdversion1.0-SNAPSHOT/version/dependency
/dependencies5.新建ApArticleContentMapper package com.heima.article.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.article.pojos.ApArticleContent;
import org.apache.ibatis.annotations.Mapper;Mapper
public interface ApArticleContentMapper extends BaseMapperApArticleContent {
}6.在artile微服务中新增测试类后期新增文章的时候创建详情静态页目前暂时手动生成 package com.heima.article.test;import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.heima.article.ArticleApplication;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.file.service.FileStorageService;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleContent;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.lang3.StringUtils;
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;import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;SpringBootTest(classes ArticleApplication.class)
RunWith(SpringRunner.class)
public class ArticleFreemarkerTest {Autowiredprivate Configuration configuration;Autowiredprivate FileStorageService fileStorageService;Autowiredprivate ApArticleMapper apArticleMapper;Autowiredprivate ApArticleContentMapper apArticleContentMapper;Testpublic void createStaticUrlTest() throws Exception {//1.获取文章内容ApArticleContent apArticleContent apArticleContentMapper.selectOne(Wrappers.ApArticleContentlambdaQuery().eq(ApArticleContent::getArticleId, 1383827952326987778L));if(apArticleContent ! null StringUtils.isNotBlank(apArticleContent.getContent())){//2.文章内容通过freemarker生成html文件StringWriter out new StringWriter();Template template configuration.getTemplate(article.ftl);//数据模型MapString, Object params new HashMap();params.put(content, JSONArray.parseArray(apArticleContent.getContent()));//合成template.process(params, out);InputStream is new ByteArrayInputStream(out.toString().getBytes());//3.把html文件上传到minio中String path fileStorageService.uploadHtmlFile(, apArticleContent.getArticleId() .html, is);//4.修改ap_article表保存static_url字段ApArticle article new ApArticle();article.setId(apArticleContent.getArticleId());article.setStaticUrl(path);apArticleMapper.updateById(article);}}
}运行上述代码会在数据库中保存static_url字段 复制该url到浏览器打开 打开MinIO Browser能够找到该html文件说明上传成功。
然后我们可以启动文章的网管、文章应用、用户应用来查看一下文章详情是否能够访问到 浏览器输入http://localhost:8801登录黑马头条
点击上面的文章跳转到 说明文章详情功能实现。