特别声明:本系列所涉及资料皆为黑马程序员课程中的资料
目录
一、前言
今天实现一下文章详情功能,昨天实现的只是文章展示在前端,点击的时候并不会展示文章内容,今天就来实现一下点击文章能够实现内容的展示。
因为文章内容有单独的一张表存储可以单机文章的时候直接进行访问,当然可行,不过每次都进行查表反而十分麻烦,接下来利用freemarker来实现文章展示功能,这个技术我也是第一次听说。具体逻辑如下。
二、Freemarker
Freemarker 是一款基于 Java 的模板引擎,主要用于生成动态文本输出(如 HTML、XML、JSON 等)。它通过将模板文件与数据模型结合,实现内容渲染,广泛应用于 Web 开发、代码生成和报表导出等场景。
1、环境搭建
(1)创建freemarker-demo 模块
创建的时候一定要看好位置和版本号
(2)引入pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- apache 对 java io 的封装工具库 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
(3)创建yml文件
server:
port: 8881 #服务端口
spring:
application:
name: freemarker-demo #指定服务名
freemarker:
cache: false #关闭模板缓存,方便测试
settings:
template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试
suffix: .ftl #指定Freemarker模板文件的后缀名
(4)创建ftl文件
(5)创建启动类
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);
}
}
(6)测试一下
整体目录结构如下
就是将学生的信息进行创建并且展示在ftl文件中,注意一下controller层代码返回的是01-basic,正是ftl文件名称,这里不需要添加后缀,因为底层已经加工过了。
访问一下页面即可在前端展现出数据
这个后缀名可以自己更改,html、ftl、ftlh、xml、jsp都行
2、基础指令和语法
这部分了解下即可,估计只有博客相关项目才会进行这样展示
(1)list相关语法
(2)map相关语法
和list差不多,看看就行了
可以看到数据全都正常展示出来了
(3)条件判断
这里做一个简单的条件判断
(4)运算符
数学运算符
以下是常用的比较运算符格式
逻辑运算符

(5)空值
加一个控制判断,就算集合为空也不会报错
(6)函数
这些函数都只是与我们的项目相关,了解一下即可
3、输出静态文件
(1)测试一下
这里就路径那里改一下就行了其实,放一个自己找得到的路径
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 {
@Autowired
private Configuration configuration;
@Test
public void test() throws IOException, TemplateException {
Template template = configuration.getTemplate("02-list.ftl");
//生成静态页面
//参数:1.数据模型 2.输出流
template.process(getData(),new FileWriter("D:\\AllCode\\java_Code\\springcloud\\heima-leadnews\\htmlAll\\test1.html"));
}
private Map getData() {
Map<String,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集合中
List<Student> stus = new ArrayList<>();
stus.add(stu1);
stus.add(stu2);
//向model中存放List集合数据
map.put("stus",stus);
//------------------------------------
//创建Map数据
HashMap<String,Student> stuMap = new HashMap<>();
stuMap.put("stu1",stu1);
stuMap.put("stu2",stu2);
// 3.1 向model中存放Map数据
map.put("stuMap",stuMap);
//3.2日期对象
map.put("today",new Date());
// 3.3 向model中存放单个数据
map.put("point",389874997498489L);
return map;
}
}
(2)可以看到文件输出出来了
打开也是和8881端口展示的页面一样
三、MinIO
Minio 是一个高性能、分布式的开源对象存储系统,专为云原生和容器化环境设计。它兼容 Amazon S3 API,支持海量非结构化数据(如图片、视频、日志文件等)的存储和管理,适用于私有云和混合云场景。
1、安装部署minio
这里启动容器的时候不知道为什么打不开网页,我又在网上找了下解决方案
docker run -d \
-p 9000:9000 \
-p 9090:9090 \
-v /mnt/minio/data:/data \
-e "MINIO_ROOT_USER=admin" \
-e "MINIO_ROOT_PASSWORD=admin123" \
--name minio-server \
minio/minio server /data --console-address ":9090"
输入账号密码就进来了
创建一个桶
2、环境搭建
(1)创建minio-demo模块
创建的时候记得看好继承的哪个模块,还有版本要选择对
(2)引入pom依赖
这个地方的版本号倒是难住我了
又去官网看了下发现是8.4.3
到这里又卡住了
只好启动最终方案了,重新安装minio
等了一会竟然可以了😂
(3)创建启动类
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);
}
}
(4)创建测试类
目前的目录结构就是这样了
(6)上传文件
这里有几个注意点,一定要修改
package com.heima.minio.test;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import java.io.FileInputStream;
public class MinIoTest {
public static void main(String[] args) {
try{
FileInputStream fileInputStream = new FileInputStream("D:\\AllCode\\java_Code\\springcloud\\heima-leadnews\\htmlAll\\test1.html");
//1.获取minio链接
MinioClient minioClient = MinioClient.builder().credentials("admin", "admin123456").endpoint("http://192.168.73.134:9000").build();
//2.上传文件
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.bucket("leadnews")//存储桶名称
.object("test1.html")//文件名称
.stream(fileInputStream, fileInputStream.available(), -1)//文件流
.build();//构建上传参数
minioClient.putObject(putObjectArgs);//上传文件
//3.打印下访问路径
System.out.println("http://192.168.73.134:9000/leadnews/test1.html");
}catch (Exception e){
e.printStackTrace();
}
}
}
(7)测试
测试的时候遇到了2个错误,第一个是版本过低
这里直接修改并不行,因为继承了springboot中的版本
要在最上面加入这么一个依赖才可以
第二个错误就是时间不照应
这个感觉挺麻烦的其实确实不简单,我一直都不知道centos中的时间竟然不同步,按着我的步骤一步一步运行即可,这里实在centos中运行的
# 1. 编辑配置文件
sudo vi /etc/chrony.conf
# 2. 重启 chronyd 服务
sudo systemctl restart chronyd
# 3. 设置开机自启
sudo systemctl enable chronyd
# 4. 检查同步状态
chronyc sources
chronyc tracking
问题解决后就可以运行测试了
可以看到上传成功了
四、封装与静态页面上传
1、封装minio为starter
(1)导入basic模块
新导入的模块要继承下父工程才会生效
导入完成后目录为这样
(2)改造minio-demo模块
在pom文件中新添加一个依赖
<dependency>
<groupId>com.heima</groupId>
<artifactId>heima-file-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
添加配置文件
minio:
accessKey: admin
secretKey: admin123456
bucket: leadnews
endpoint: http://192.168.73.134:9000
readPath: http://192.168.73.134:9000
创建测试方法
这里用到了starter中的方法
package com.heima.minio.test;
import com.heima.file.service.FileStorageService;
import com.heima.minio.MinIOApplication;
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 {
@Autowired
private FileStorageService fileStorageService;
@Test
public void test() throws FileNotFoundException {
FileInputStream fileInputStream = new FileInputStream("D:\\AllCode\\java_Code\\springcloud\\heima-leadnews\\htmlAll\\test1.html");
String path = fileStorageService.uploadHtmlFile("", "test1.html", fileInputStream);
System.out.println(path);
}
// public static void main(String[] args) {
// try{
// FileInputStream fileInputStream = new FileInputStream("D:\\AllCode\\java_Code\\springcloud\\heima-leadnews\\htmlAll\\test1.html");
// //1.获取minio链接
// MinioClient minioClient = MinioClient.builder().credentials("admin", "admin123456").endpoint("http://192.168.73.131:9000").build();
//
// //2.上传文件
// PutObjectArgs putObjectArgs = PutObjectArgs.builder()
// .bucket("leadnews")//存储桶名称
// .object("test1.html")//文件名称
// .stream(fileInputStream, fileInputStream.available(), -1)//文件流
// .build();//构建上传参数
// minioClient.putObject(putObjectArgs);//上传文件
// //3.打印下访问路径
// System.out.println("http://192.168.73.131:9001/leadnews/test1.html");
// }catch (Exception e){
// e.printStackTrace();
// }
// }
}
(3)测试
如果还出现时间问题就强制同步时间
sudo chronyc makestep
不知道怎么修改读写模式,所以无法展示/(ㄒoㄒ)/~~
2、静态文件上传
(1)引入相关依赖
(2)配置nacos
因为minio也是公用配置所以就直接配置到nacos中了,找到文章相关的这个模块加入minio配置即可
(3)改造article
先将ftl文件拷贝过来
再将页面文件上传过来
这里改一下路径和文件类型就行了
js文件也是如此,将原本的css改为js就行了
可以看到桶中出现了这两个文件
3、完善上传功能
刚才是在测试中进行的,这次在模块中完成文件上传功能
(1)定义mapper层接口
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 BaseMapper<ApArticleContent> {
}
(2)编写方法
这个方法有点过于复杂了点,我目前有点看不明白
package com.heima.article.test;
import com.alibaba.cloud.commons.lang.StringUtils;
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.service.ApArticleService;
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 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.ByteArrayInputStream;
import java.io.IOException;
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 {
@Autowired
private ApArticleContentMapper apArticleContentMapper;
@Autowired
private FileStorageService fileStorageService;
@Autowired
private Configuration configuration;
@Autowired
private ApArticleService apArticleService;
@Test
public void createStaticUrlTest() throws IOException, TemplateException {
//1.获取文章内容
ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, "1302862387124125698L"));
if(apArticleContent != null && StringUtils.isNotBlank(apArticleContent.getContent())){
//2.创建静态页面
Template template = configuration.getTemplate("article.ftl");
Map<String,Object> content = new HashMap<>();
content.put("content", JSONArray.parseArray(apArticleContent.getContent()));
StringWriter out = new StringWriter();
template.process(content,out);
//3.保存静态页面上传到minio中
InputStream in = new ByteArrayInputStream(out.toString().getBytes());
String path = fileStorageService.uploadHtmlFile("", apArticleContent.getId() + ".html", in);
//4.修改ap_article,保存static_url字段
apArticleService.update(Wrappers.<ApArticle>lambdaUpdate().eq(ApArticle::getId,apArticleContent.getArticleId()).set(ApArticle::getStaticUrl,path));
}
}
}
(3)测试
测试过程中遇到了一个错误
我真服了,仓库受损,被这些杂乱的错误搞崩溃了!!!