2024/9/4黑马头条跟学笔记(二)

app端文章列表

学习内容

需求分析

上方分类频道切换

布局,无图,单图,三张图

image-20240903113610243

文章数据库表

导入文章数据库

结构分析

image-20240903112319319

image-20240903112344716

配置-文章 一对一,拆表,冷热数据分离满足范式

表的拆分-垂直分表

image-20240903113812993

优势

查文章信息不会连带查询文章内容

将longtext字段拆除去,高频查询提高性能

拆分规则

  1. 不常用的放一起

  2. text,blob放一起

  3. 组合查询字段放一起

拷贝实体类

dtos包下
 package com.heima.model.article.dtos;​import lombok.Data;​import java.util.Date;​@Datapublic class ArticleHomeDto {​// 最大时间Date maxBehotTime;// 最小时间Date minBehotTime;// 分页sizeInteger size;// 频道IDString tag;}​
pojos包下
 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;}​
 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;}​
 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;}​

sql实现思路

默认展示10条文章

切换频道

下拉刷新,新数据必须大于当前10条数据最新的那条,由于是降序排列,时间大于第一条,《什么是java语言》

上拉加载,新数据必须小于当前10条数据最晚那条,也就是最下面的那条信息,因为要加载早期发布的,所以其时间戳要小一点

如果为首页,就展示十条最新数据即可,时间戳小于2063年

查询语句

 SELECT * from ap_article aa LEFT JOIN ap_article_config aac ON aa.id=aac.article_idWHERE publish_time >'2020-09-08'AND channel_id=1AND aac.is_down!=1AND aac.is_delete!=1ORDER BY created_time DESCLIMIT 10 

最新10,加载老一点的10,左连接条件约束不能下架且不能删除,降序查

接口实现

最大时间,最小时间,分页大小,频道id

image-20240903122047409

步骤

image-20240903122117353

注意,解压缩后的目录不能直接复制粘贴,外面多了一层同名的目录

导入微服务

image-20240903143408154

image-20240903150143001

导入成功后的文件前面有个小蓝点,否则在里面创建不了class

nacos添加配置

image-20240903143706407

 spring:datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/leadnews_article?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTCusername: rootpassword: 123456# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置mybatis-plus:mapper-locations: classpath*:mapper/*.xml# 设置别名包扫描路径,通过该属性可以给包中的类注册别名type-aliases-package: com.heima.model.article.pojos​

路由服务增加新的服务

 spring:cloud:gateway:globalcors:add-to-simple-url-handler-mapping: truecorsConfigurations:'[/**]':allowedHeaders: "*"allowedOrigins: "*"allowedMethods:- GET- POST- DELETE- PUT- OPTIONroutes:# 平台管理- id: useruri: lb://leadnews-userpredicates:- Path=/user/**filters:- StripPrefix= 1# 文章微服务- id: articleuri: lb://leadnews-articlepredicates:- Path=/article/**filters:- StripPrefix= 1​

定义接口

 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 {​​@PostMapping("/load")public ResponseResult load(@RequestBody ArticleHomeDto dto) {return null;}​@PostMapping("/loadmore")public ResponseResult loadMore(@RequestBody ArticleHomeDto dto) {return null;}​@PostMapping("/loadnew")public ResponseResult loadNew(@RequestBody ArticleHomeDto dto) {return null;}}​

编写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;​@Mapperpublic interface ApArticleMapper extends BaseMapper<ApArticle> {/*** 加载文章列表 1为加载更多,2为加载最新* @param dto* @param type* @return*/​public List<ApArticle> loadArticleList(@Param("dto") ArticleHomeDto dto, @Param("type") Short type);​}​

编写xml

(resources/mapper/ApArticleMapper.xml)

 <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.heima.article.mapper.ApArticleMapper">​<resultMap id="resultMap" type="com.heima.model.article.pojos.ApArticle"><id column="id" property="id"/><result column="title" property="title"/><result column="author_id" property="authorId"/><result column="author_name" property="authorName"/><result column="channel_id" property="channelId"/><result column="channel_name" property="channelName"/><result column="layout" property="layout"/><result column="flag" property="flag"/><result column="images" property="images"/><result column="labels" property="labels"/><result column="likes" property="likes"/><result column="collection" property="collection"/><result column="comment" property="comment"/><result column="views" property="views"/><result column="province_id" property="provinceId"/><result column="city_id" property="cityId"/><result column="county_id" property="countyId"/><result column="created_time" property="createdTime"/><result column="publish_time" property="publishTime"/><result column="sync_status" property="syncStatus"/><result column="static_url" property="staticUrl"/></resultMap><select id="loadArticleList" resultMap="resultMap">SELECTaa.*FROM`ap_article` aaLEFT JOIN ap_article_config aac ON aa.id = aac.article_id<where>and aac.is_delete != 1and aac.is_down != 1<!-- loadmore --><if test="type != null and type == 1">and aa.publish_time <![CDATA[<]]> #{dto.minBehotTime}</if><if test="type != null and type == 2">and aa.publish_time <![CDATA[>]]> #{dto.maxBehotTime}</if><if test="dto.tag != '__all__'">and aa.channel_id = #{dto.tag}</if></where>order by aa.publish_time desclimit #{dto.size}</select>​</mapper>​
 <if test="dto.tag != '__all__'">and aa.channel_id = #{dto.tag}</if>

意思为不为推荐页,则按频道分类

常量

由于1,2难分辨代表啥意思,我们抽成常量类

 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__";​}​

业务层

ApArticleService

 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;​public interface ApArticleService extends IService<ApArticle> {​/*** 加载文章列表* @param dto* @param type  1为加载更多,2为加载最新* @return*/public ResponseResult load(ArticleHomeDto dto,Short type);}​

ApArticleServiceImpl

 @Servicepackage com.heima.article.service.impl;​import com.alibaba.cloud.commons.lang.StringUtils;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 org.springframework.beans.factory.annotation.Autowired;​import java.util.Date;import java.util.List;​public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {​@AutowiredApArticleMapper apArticleMapper;​/*** 加载文章列表** @param dto* @param type 1为加载更多,2为加载最新* @return*/@Overridepublic ResponseResult load(ArticleHomeDto dto, Short loadtype) {​//参数校验​//判断大小是否正确Integer size = dto.getSize();if(size==null||size==0){size=Math.min(size,50);}//类型参数检验,既不为1,加载更多也不为2加载最新,那么就默认1加载更多if(!loadtype.equals(ArticleConstants.LOADTYPE_LOAD_MORE)&&!loadtype.equals(ArticleConstants.LOADTYPE_LOAD_NEW)){loadtype = ArticleConstants.LOADTYPE_LOAD_MORE;}//文章频道校验,如果不指定频道,那就是首页,直接加载最新10条if(StringUtils.isEmpty(dto.getTag())){dto.setTag(ArticleConstants.DEFAULT_TAG);}//时间校验。如果没有最大和最小时间,那么说明时间范围为无限,此时降序展示10条最新数据,与前面的Tag频道搭配if(dto.getMaxBehotTime() == null) dto.setMaxBehotTime(new Date());if(dto.getMinBehotTime() == null) dto.setMinBehotTime(new Date());​//2.查询数据List<ApArticle> apArticles = apArticleMapper.loadArticleList(dto, loadtype);return ResponseResult.okResult(apArticles);}}​

接口完善

 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(dto, ArticleConstants.LOADTYPE_LOAD_MORE);}​@PostMapping("/loadmore")public ResponseResult loadMore(@RequestBody ArticleHomeDto dto) {return apArticleService.load(dto, ArticleConstants.LOADTYPE_LOAD_MORE);}​@PostMapping("/loadnew")public ResponseResult loadNew(@RequestBody ArticleHomeDto dto) {return apArticleService.load(dto, ArticleConstants.LOADTYPE_LOAD_NEW);}}​

路由服务增加转发列表

在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​

测试加载更多和最新

app文章详情

实现方案一

image-20240903211243493

根据id查

实现方案2

image-20240903211430150

内容通过模板技术生成html文件

存入分布式系统minIO,同时有一个url存入数据库

用户直接访问url,不需要查库

大公司做longtext和blob时使用

freemarker

介绍

模板引擎

模板+数据生成HTML

模板编写全拼为 freemarker template language FTL

image-20240904140355892

常见java模板引擎

image-20240904140900708

jsp(javaWeb里的servlet,前后端不分离),

freemarker 好,快

thymeleaf 好,但慢

velocity 不好

作为springmvc视图格式,且支持渲染视图

环境搭建

在测试模块下创建子工程,并添加依赖

依赖

 <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>

application.yml配置文件

 server:port: 8881 #服务端口spring:application:name: freemarker-demo #指定服务名freemarker:cache: false  #关闭模板缓存,方便测试settings:template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试suffix: .ftl               #指定Freemarker模板文件的后缀名​

关缓存,修改数据实时更新

启动类

 package com.heima.freemarker;​import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;​import javax.swing.*;​@SpringBootApplicationpublic class FreeMarkerDemoApplication {public static void main(String[] args) {SpringApplication.run(FreeMarkerDemoApplication.class, args);}}​

实体类

 package com.heima.freemarker.entity;​import lombok.Data;​import java.util.Date;​@Datapublic class Student {private String name;//姓名private int age;//年龄private Date birthday;//生日private Float money;//钱包}​

在resources下创建templates文件夹,存入模板文件

image-20240904142204443

对象啥数据,模板就啥数据

 <!DOCTYPE html><html><head><meta charset="utf-8"><title>Hello World!</title></head><body><b>普通文本 String 展示:</b><br><br>Hello ${name} <br><hr><b>对象Student中的数据展示:</b><br/>姓名:${stu.name}<br/>年龄:${stu.age}<hr></body></html>​

接口

 package com.heima.freemarker.controller;​​import com.heima.freemarker.entity.Student;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.GetMapping;​// 此除不用restController,返回视图而非字符串,使用controller即可@Controllerpublic class HelloController {@GetMapping("/basic")public String hello(Model model) {// namemodel.addAttribute("name", "hello freemarker");// stuStudent student = new Student();student.setAge(18);student.setName("小明");model.addAttribute("stu", student);return "01-basic";}}​

访问测试

Hello World!

image-20240904142836287

如何替换的

加了依赖,相关类在启动时加到了spring容器,默认配置后缀ftlh(不过通常以ftl结尾),模板路径classpath/templates

image-20240904143317385

image-20240904143132288

image-20240904143402345

指令语法

image-20240904143422251

image-20240904145945650

集合指令

增加接口

 @GetMapping("/list")public String list(Model model){​//------------------------------------Student stu1 = new Student();stu1.setName("A");stu1.setAge(18);stu1.setMoney(666f);stu1.setBirthday(new Date());​//小红对象模型数据Student stu2 = new Student();stu2.setName("B");stu2.setMoney(999f);stu2.setAge(19);​//将两个对象模型数据存放到List集合中List<Student> stus = new ArrayList<>();stus.add(stu1);stus.add(stu2);​//向model中存放List集合数据model.addAttribute("stus",stus);​//------------------------------------​//创建Map数据HashMap<String,Student> stuMap = new HashMap<>();stuMap.put("stu1",stu1);stuMap.put("stu2",stu2);// 3.1 向model中存放Map数据model.addAttribute("stuMap", stuMap);​return "02-list";}

增加模板文件list.ftl

 <!DOCTYPE html><html><head><meta charset="utf-8"><title>Hello World!</title></head><body>​<#-- list 数据的展示 --><b>展示list中的stu数据:</b><br><br><table><tr><td>序号</td><td>姓名</td><td>年龄</td><td>钱包</td></tr><#list stus as s><tr><td>${s_index+1}</td><td>${s.name}</td><td>${s.age}</td><td>${s.money}</td></tr></#list></table><hr>​<#-- Map 数据的展示 --><b>map数据的展示:</b><br/><br/><a href="###">方式一:通过map['keyname'].property</a><br/>输出stu1的学生信息:<br/>姓名:<br/>${stuMap.stu1.name}年龄:<br/>${stuMap.stu1.age}<br/><a href="###">方式二:通过map.keyname.property</a><br/>输出stu2的学生信息:<br/>姓名:<br/>${stuMap["stu2"].name}年龄:<br/>${stuMap["stu2"].age}​<br/><a href="###">遍历map中两个学生信息:</a><br/><table><tr><td>序号</td><td>姓名</td><td>年龄</td><td>钱包</td></tr><#list stuMap?keys as key><tr><td>${key_index+1}</td><td>${stuMap[key].name}</td><td>${stuMap[key].age}</td><td>${stuMap[key].money}</td></tr></#list></table><hr>​</body></html>​

<#list stus as s>遍历

子标签 ${s.name}获取值

获取索引值为s_index也可以+1处理

map值获取用stu.["学生1"].age

或者用stu.学生1.age

遍历则使用kes as key

然后key为每个对象,用上述方法即可

if指令

  <#if s.name=="A"><tr style="color: red"><td>${s_index+1}</td><td>${s.name}</td><td>${s.age}</td><td>${s.money}</td></tr><#else><tr><td>${s_index+1}</td><td>${s.name}</td><td>${s.age}</td><td>${s.money}</td></tr></#if>

=与==号用法一致

image-20240904152811499

运算符

image-20240904153135102

image-20240904153152589

gt代替> 可能会被当作结束字符<#if (x>y)>

image-20240904153445644

空值处理

防止报错,代码健壮性

image-20240904153517170

外层嵌套

  <#if stus??></#if>

变量空值处理

 ${name!""}

加了个!""

也可以在""里加入你想要填入的默认值

 ${name!"============"}

image-20240904154016498

image-20240904154058301

内建函数

用法

集合大小

 ${变量+?+函数名称}

image-20240904154323777

日期格式化+去除每三位生成的一逗号

 ${today?datetime}<br>${today?string("yyyy年MM月")}<br>${score?c}
         model.addAttribute("today", new Date());model.addAttribute("score", 8823333333333333f);

image-20240904154911866

json转为字符串

image-20240904155051538

image-20240904155116297

静态文件生成

配置文件增加引用路径

 template-loader-path: classpath:/templates   #模板存放位置

新建测试类,引入freemarker上下文

image-20240904160659991

 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 {// 1.获取模板对象通过文件名搭配配置文件寻找Template template = configuration.getTemplate("02-list.ftl");// 2.使用该对象合成模型// 参数1为模板文件。2为输出流,指定生成文件的位置template.process(getData(), new FileWriter("d:/02-list.html"));}​​private Map getData() {//------------------------------------Map<String, Object> map = new HashMap<>();Student stu1 = new Student();stu1.setName("A");stu1.setAge(18);stu1.setMoney(666f);stu1.setBirthday(new Date());​// 小红对象模型数据Student stu2 = new Student();stu2.setName("B");stu2.setMoney(999f);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);map.put("today", new Date());map.put("score", 8823333333333333f);return map;}}​

运行生成模板文件

image-20240904160633313

MinIO

对象存储方式对比

image-20240904160844168

服务器,爆内存要扩麻烦

分布式,构建复杂

第三方,贵,裤兜子里没钢镚买不起

分布式文件系统

minIO nb!

概述

image-20240904161122373

开源,保存大空间

单行命令运行

支持云存储迁移接口,可控大小

环境搭建

image-20240904161241028

拉取镜像

 docker pull minio/minio

创建容器

 docker run -p 9000:9000 --name minio -d --restart=always -e "MINIO_ROOT_USER=minio" -e "MINIO_ROOT_PASSWORD=minio123" -v /home/data:/data -v /home/config:/root/.minio minio/minio server /data ​

开机启动

用户名minio 密码minio123

访问地址 http://192.168.233.136:9000

image-20240904165141276

如果是自己下的镜像那么就会出现上述错误

黑马给我们的古老的minio环境变量产生了错误

修改为MINIO_ROOT_USER MINIO_ROOT_PASSWORD且以下面这条代码启动

  • 映射了内部端口的9001和9000 9000为api,9001为控制台

  • 控制台端口--console-address ":9001" 必须加不加访问不了

    在 MinIO 中,--console-address ":9001" 参数用于指定控制台的访问地址和端口。如果不指定这个参数,控制台将不会自动启动,或可能使用默认设置,导致无法访问。

    主要原因:

    1. 控制台未启用:

      • 默认情况下,MinIO 可能不会启动控制台,除非明确指定其地址和端口。

 docker run -p 9000:9000 \-p 9001:9001 --name minio -d --restart=always -e "MINIO_ROOT_USER=minio" -e "MINIO_ROOT_PASSWORD=minio123" -v /home/data:/data -v /home/config:/root/.minio minio/minio server /data --console-address ":9001"

基本概念

image-20240904171200479

bucket 图片根目录文件夹

object html文件等等...

keys 文件名

点+建桶 由于黑马的镜像太大 ,自带虚拟机和镜像后续操作版本不和黑马的minio操作一致,不过原理差不多

image-20240904171312732

快速入门

image-20240904171626385

上传list.html到minio 并且访问

创建minio-demo模块

依赖引入

 <dependencies>​<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>7.1.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>​<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency></dependencies>

引导类

 package com.heima.minio;​import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;​​@SpringBootApplicationpublic class MinIOApplication {​public static void main(String[] args) {SpringApplication.run(MinIOApplication.class,args);}}​

上传业务类

 package com.heima.minio;​import io.minio.MinioClient;import io.minio.PutObjectArgs;​import javax.imageio.stream.FileImageInputStream;import java.io.FileInputStream;import java.io.FileNotFoundException;​public class MinIOTest {public static void main(String[] args) {//获取文件流,后续传输到服务器try {FileInputStream inputStream = new FileInputStream("D:\\02-list.html");// 1 创建minio客户端输入密码账号MinioClient minioClient = MinioClient.builder().credentials("minio", "minio123").endpoint("http://192.168.233.139:9000").build();// 2 上传文件// 构建上传配置参数PutObjectArgs putObjectArgs = PutObjectArgs.builder().object("list.html")//文件名.contentType("text/html")//文件类型.stream(inputStream, inputStream.available(), -1)//文件流.bucket("leadnews")//桶名.build();// 放入客户端minioClient.putObject(putObjectArgs);// 成功则打印访问地址System.out.println("http://192.168.233.139:9000/leadnews/list.html");} catch (Exception e) {throw new RuntimeException(e);}​}}​

访问连接报错,提示拒绝访问access denied

image-20240904182448412

最新版得设置access权限

image-20240904182424167

访问成功

image-20240904182532896

封装Minio为starter

image-20240904182935567

不需要再每一个微服务里集成,做成一个公共的微服务

步骤

image-20240904183036800

导入公共微服务,文件管理starter

该微服务导入文件管理依赖,添加配置文件,注入service后续调用

父工程添加pom将其纳入子模块

 heima-leadnews-basic

image-20240904183520817

然后刷新maven 成功变蓝

image-20240904183558712

项目集成

minio里依赖引入file服务

image-20240904183939325

 <dependency><groupId>com.heima</groupId><artifactId>heima-file-starter</artifactId><version>1.0-SNAPSHOT</version></dependency>​

minio-test配置文件

application.yml

 minio:accessKey: miniosecretKey: minio123bucket: leadnewsendpoint: http://192.168.233.136:9000readPath: http://192.168.233.136:9000

一一对应(prefix标识读取在配置文件里minio的值)

image-20240904184625223

注入FIlestorageService调用测试

 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):// 指定了Spring Boot的启动类,用于加载Spring上下文。​// @RunWith(SpringRunner.class):指定了JUnit的运行器,用于在Spring环境中运行测试@SpringBootTest(classes = MinIOApplication.class)@RunWith(SpringRunner.class)public class MinIOTest {​@Autowiredprivate FileStorageService fileStorageService;​@Testpublic void test() throws FileNotFoundException {FileInputStream inputStream = new FileInputStream("D:\\02-list.html");String path = fileStorageService.uploadHtmlFile("", "list.html", inputStream);System.out.println(path);}​// public static void main(String[] args) {//     //获取文件流,后续传输到服务器//     try {//         FileInputStream inputStream = new FileInputStream("D:\\02-list.html");//         // 1 创建minio客户端输入密码账号//         MinioClient minioClient = MinioClient.builder().credentials("minio", "minio123").endpoint("http://192.168.233.136:9000").build();//         // 2 上传文件//         // 构建上传配置参数//         PutObjectArgs putObjectArgs = PutObjectArgs.builder()//                 .object("list.html")//文件名//                 .contentType("text/html")//文件类型//                 .stream(inputStream, inputStream.available(), -1)//文件流//                 .bucket("leadnews")//桶名//                 .build();//         // 放入客户端//         minioClient.putObject(putObjectArgs);//         // 成功则打印访问地址//         System.out.println("http://192.168.233.136:9000/leadnews/list.html");//     } catch (Exception e) {//         throw new RuntimeException(e);//     }//// }}​

这种方法上传的会产生时间目录

image-20240904201712321

image-20240904201817254

回顾一下

ftl技术 存入minio文件系统,

并存url路径

用户访问该html文件

静态页面生成上传

image-20240904203339479

mapper&&mapperxml

 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;​@Mapperpublic interface ApArticleContentMapper extends BaseMapper<ApArticleContent> {}​

article服务添加freemarker依赖和file-starter

  <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId></dependency><dependency><groupId>com.heima</groupId><artifactId>heima-file-starter</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>

将minio-demo的配置文件迁移到article服务

 minio:accessKey: miniosecretKey: minio123bucket: leadnewsendpoint: http://192.168.233.136:9000readPath: http://192.168.233.136:9000

image-20240904204029256

创建模板文件

article.ftl

 <!DOCTYPE html><html>​<head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport"content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover"><title>黑马头条</title><!-- 引入样式文件 --><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vant@2.12.20/lib/index.css"><!-- 页面样式 --><link rel="stylesheet" href="../../../plugins/css/index.css"></head>​<body><div id="app"><div class="article"><van-row><van-col span="24" class="article-title" v-html="title"></van-col></van-row>​<van-row type="flex" align="center" class="article-header"><van-col span="3"><van-image round class="article-avatar" src="https://p3.pstatp.com/thumb/1480/7186611868"></van-image></van-col><van-col span="16"><div v-html="authorName"></div><div>{{ publishTime | timestampToDateTime }}</div></van-col><van-col span="5"><van-button round :icon="relation.isfollow ? '' : 'plus'" type="info" class="article-focus":text="relation.isfollow ? '取消关注' : '关注'" :loading="followLoading" @click="handleClickArticleFollow"></van-button></van-col></van-row>​<van-row class="article-content"><#if content??><#list content as item><#if item.type='text'><van-col span="24" class="article-text">${item.value}</van-col><#else><van-col span="24" class="article-image"><van-image width="100%" src="${item.value}"></van-image></van-col></#if></#list></#if></van-row>​<van-row type="flex" justify="center" class="article-action"><van-col><van-button round :icon="relation.islike ? 'good-job' : 'good-job-o'" class="article-like":loading="likeLoading" :text="relation.islike ? '取消赞' : '点赞'" @click="handleClickArticleLike"></van-button><van-button round :icon="relation.isunlike ? 'delete' : 'delete-o'" class="article-unlike":loading="unlikeLoading" @click="handleClickArticleUnlike">不喜欢</van-button></van-col></van-row>​<!-- 文章评论列表 --><van-list v-model="commentsLoading" :finished="commentsFinished" finished-text="没有更多了"@load="onLoadArticleComments"><van-row id="#comment-view" type="flex" class="article-comment" v-for="(item, index) in comments" :key="index"><van-col span="3"><van-image round src="https://p3.pstatp.com/thumb/1480/7186611868" class="article-avatar"></van-image></van-col><van-col span="21"><van-row type="flex" align="center" justify="space-between"><van-col class="comment-author" v-html="item.authorName"></van-col><van-col><van-button round :icon="item.operation === 0 ? 'good-job' : 'good-job-o'" size="normal"@click="handleClickCommentLike(item)">{{ item.likes || '' }}</van-button></van-col></van-row>​<van-row><van-col class="comment-content" v-html="item.content"></van-col></van-row><van-row type="flex" align="center"><van-col span="10" class="comment-time">{{ item.createdTime | timestampToDateTime }}</van-col><van-col span="3"><van-button round size="normal" v-html="item.reply" @click="showCommentRepliesPopup(item.id)">回复 {{item.reply || '' }}</van-button></van-col></van-row></van-col></van-row></van-list></div><!-- 文章底部栏 --><van-row type="flex" justify="space-around" align="center" class="article-bottom-bar"><van-col span="13"><van-field v-model="commentValue" placeholder="写评论"><template #button><van-button icon="back-top" @click="handleSaveComment"></van-button></template></van-field></van-col><van-col span="3"><van-button icon="comment-o" @click="handleScrollIntoCommentView"></van-button></van-col><van-col span="3"><van-button :icon="relation.iscollection ? 'star' : 'star-o'" :loading="collectionLoading"@click="handleClickArticleCollection"></van-button></van-col><van-col span="3"><van-button icon="share-o"></van-button></van-col></van-row>​<!-- 评论Popup 弹出层 --><van-popup v-model="showPopup" closeable position="bottom":style="{ width: '750px', height: '60%', left: '50%', 'margin-left': '-375px' }"><!-- 评论回复列表 --><van-list v-model="commentRepliesLoading" :finished="commentRepliesFinished" finished-text="没有更多了"@load="onLoadCommentReplies"><van-row id="#comment-reply-view" type="flex" class="article-comment-reply"v-for="(item, index) in commentReplies" :key="index"><van-col span="3"><van-image round src="https://p3.pstatp.com/thumb/1480/7186611868" class="article-avatar"></van-image></van-col><van-col span="21"><van-row type="flex" align="center" justify="space-between"><van-col class="comment-author" v-html="item.authorName"></van-col><van-col><van-button round :icon="item.operation === 0 ? 'good-job' : 'good-job-o'" size="normal"@click="handleClickCommentReplyLike(item)">{{ item.likes || '' }}</van-button></van-col></van-row>​<van-row><van-col class="comment-content" v-html="item.content"></van-col></van-row><van-row type="flex" align="center"><!-- TODO: js计算时间差 --><van-col span="10" class="comment-time">{{ item.createdTime | timestampToDateTime }}</van-col></van-row></van-col></van-row></van-list><!-- 评论回复底部栏 --><van-row type="flex" justify="space-around" align="center" class="comment-reply-bottom-bar"><van-col span="13"><van-field v-model="commentReplyValue" placeholder="写评论"><template #button><van-button icon="back-top" @click="handleSaveCommentReply"></van-button></template></van-field></van-col><van-col span="3"><van-button icon="comment-o"></van-button></van-col><van-col span="3"><van-button icon="star-o"></van-button></van-col><van-col span="3"><van-button icon="share-o"></van-button></van-col></van-row></van-popup></div>​<!-- 引入 Vue 和 Vant 的 JS 文件 --><script src=" https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script><script src="https://cdn.jsdelivr.net/npm/vant@2.12.20/lib/vant.min.js"></script><!-- 引入 Axios 的 JS 文件 --><#--<script src="https://unpkg.com/axios/dist/axios.min.js"></script>--><script src="../../../plugins/js/axios.min.js"></script><!-- 页面逻辑 --><script src="../../../plugins/js/index.js"></script></body>​</html>

数据库content

image-20240904204510275

类型 和 值 ,text文本类型,一种样式, img图片类型,另一种样式

image-20240904204543622

上传基础css,js

tip:js(修改地址)

image-20240904205500233

image-20240904205358124

依次上传js和css文件

image-20240904205603690

新建测试类

感叹一下,牛逼,只有当文章修改时需要重新查询数据库,日常的无数次给用户查询都访问静态页面,大大减轻了数据库的压力,一张表存longtext内容,一张表存基础信息

思路

  1. 查库获取content(次数很少,只有新增和修改会查)

  2. 不为空则通过模板文件生成html。获取模板

  3. 填充数据模型,由于模板里的list content名字固定,这里到时候的map属性值也的是content,且由于数据库存字符串,其值要求对象,json转对象

  1. 使用stringwrite (out)将html文件暂存

  2. 上传minio时将(out)参数传入,不过这里需要字节流,将字符流转为string再获取字节即可,第二个参数为文章id加html

  3. 返回的路径设置到article表的static url字段(articleService根据文章id)值为url

 package test;​import com.alibaba.fastjson.JSONArray;import com.baomidou.mybatisplus.core.conditions.Wrapper;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.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 org.apache.commons.lang3.StringUtils;import org.checkerframework.checker.units.qual.A;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 {​// 注入template模板类@Autowiredprivate Configuration configuration;// 文件上传类@Autowiredprivate FileStorageService fileStorageService;@Autowiredprivate ApArticleContentMapper apArticleContentMapper;@Autowiredprivate ApArticleMapper apArticleMapper;​@Testpublic void createStaticUrlTest() throws Exception {//1.获取文章内容ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, 1383827787629252610L));if(apArticleContent != null && StringUtils.isNotBlank(apArticleContent.getContent())){//2.文章内容通过freemarker生成html文件StringWriter out = new StringWriter();Template template = configuration.getTemplate("article.ftl");​Map<String, 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);​​}}}​

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

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

相关文章

Day10_0.1基础学习MATLAB学习小技巧总结(10)——程序流程控制

利用空闲时间把碎片化的MATLAB知识重新系统的学习一遍&#xff0c;为了在这个过程中加深印象&#xff0c;也为了能够有所足迹&#xff0c;我会把自己的学习总结发在专栏中&#xff0c;以便学习交流。 素材来源“数学建模清风” 特此说明&#xff1a;本博客的内容只在于总结在…

页面小组件-搜索栏(一)

样例展示 效果示例-折叠状态 效果示例-展开状态 代码示例 <custom-search-wrapper><!--showFoldBtn 需要展示折叠按钮时传值--><template slotleft><el-form:model"searchFormData"inlinesize"small"><el-form-item><e…

前端入门了解

1. 网页 1.1 网页概述 1.2 超文本标记语言 1.3 网页的形成 2. 浏览器了解 网页需要通过浏览器来展示&#xff0c;下面是关于浏览器的两点; 国际上通用的浏览器有如下六个&#xff08;百度&#xff0c;360&#xff0c;uc等是主要在国内使用&#xff09;&#xff0c; 3. We…

828华为云征文:华为云 Flexus X 实例性能测评——SuperBench 一键窥见性能

今天我拿到了华为云 Flexus X 实例&#xff0c;这款云服务是华为云推出的有一款明星产品&#xff0c;面向零售、金融、游戏等行业大多数通用工作负载场景。这次&#xff0c;我们就来测评一下它的性能到底怎么样&#xff01; Flexus 云服务 X 实例 在测评之前&#xff0c;我们…

使用 JAXB 将内嵌的JAVA对象转换为 xml文件

使用 JAXB 将内嵌的JAVA对象转换为 xml文件 1. 需求2. 实现&#xff08;1&#xff09;FileDesc类&#xff08;2&#xff09;MetaFileXml类&#xff08;3&#xff09;生成对应的xml文件 1. 需求 获取一个目录下所有文件的元数据信息&#xff08;文件名、大小、后缀等&#xff0…

Day14_0.1基础学习MATLAB学习小技巧总结(14)——字符串的比较、查找和替换

利用空闲时间把碎片化的MATLAB知识重新系统的学习一遍&#xff0c;为了在这个过程中加深印象&#xff0c;也为了能够有所足迹&#xff0c;我会把自己的学习总结发在专栏中&#xff0c;以便学习交流。 素材来源“数学建模清风” 特此说明&#xff1a;本博客的内容只在于总结在…

香港一带一路研究院国际事务研究中心副主任陈景才阐述香港在一带一路建设及区块链金融领域的关键作用

2024年8月28日&#xff0c;香港金管局举行Ensemble项目沙盒&#xff08;以下简称沙盒&#xff09;启动仪式&#xff0c;并宣布首阶段试验将涵盖四大代币化资产用例主题&#xff0c;标志着金融业在代币化技术的实际应用进程中迈出重要一步。香港一带一路研究院国际事务研究中心副…

Tomato靶场渗透测试

1.扫描靶机地址 可以使用nmap进行扫描 由于我这已经知道靶机地址 这里就不扫描了 2.打开网站 3.进行目录扫描 dirb http&#xff1a;//172.16.1.113 发现有一个antibot_image目录 4.访问这个目录 可以看到有一个info.php 5.查看页面源代码 可以发现可以进行get传参 6.…

苹果抽佣30%,国产手机抽佣50%,而且国产手机联合抽佣更霸道!

随着苹果抽佣30%被炒作&#xff0c;吐槽苹果的越来越多&#xff0c;然而国产手机抽佣50%&#xff0c;却没有人说话&#xff0c;甚至还有人为国产手机辩护&#xff0c;说什么可以自由选择&#xff0c;然而他们忘记了国产手机联合成立了硬核联盟共同抽佣50%&#xff0c;想逃&…

c++162 类的封装和访问

怎么样管理类管理对象 类如何定义对象 #include<iostream> using namespace std;//求圆的面积 class MyCirecle { public:double m_r;//属性 成员变量double m_s; public :double getR(){return m_r;}void setR(double r)//成员函数{m_r r;}double getS(){m_s 3.14…

国庆主题——html5+css+js国庆头像框生成

国庆主题——html5cssjs国庆头像框生成 一、前言二、功能展示四、其它五、源码下载 一、前言 国庆头像生成器这个工具可以让我们制作最近非常火爆的国庆主题头像&#xff0c;有多重不同的风格供我们选择&#xff0c;我们可以在这里在线制作头像。 二、功能展示 如下所示&…

基于SSM+MySQL的民宿推荐系统

系统背景 随着经济发展&#xff0c;各类电子产品普及千家万户。网民数量不断增加&#xff0c;网络显然已经成为了人际交流的重要形式。回顾近一个世纪的科技发展史&#xff0c;各类新的信息发布手段均随着时代洪流更新。旧时代是广播&#xff0c;报纸&#xff0c;电视&#xff…

uniapp和vue3中使用vConsole在H5中开启移动端调试

uniapp和vue3中使用vConsole在H5中开启移动端调试 1. 安装vconsole npm install vconsole --save2. 在main.js中全局引入 重新启动项目即可

昂科烧录器支持Fortior Tech峰岹科技的电机驱动专用芯片FU6812V

芯片烧录行业领导者-昂科技术近日发布最新的烧录软件更新及新增支持的芯片型号列表&#xff0c;其中Fortior Tech峰岹科技的高性能电机驱动专用芯片FU6812V已经被昂科的通用烧录平台AP8000所支持。 FU6812V是一款集成电机控制引擎(ME)和8051内核的高性能电机驱动专用芯片&…

Android Auto未来可能支持无线电广播

通过Android Auto&#xff0c;可以在车载收音机上使用 Google 地图、音乐、收听播客&#xff0c;还能获取天气等基本信息。最近&#xff0c;国外科技媒体9to5Google通过分析 Android Auto v12.3 和 v12.4的应用程序的代码发现了一些提示信息&#xff0c;特别提到了 AM、FM、HD …

信创实践(2):利用Leapp工具迁移CentOS至AnolisOS,实现系统升级与自主可控

1. 引言 为了满足用户在CentOS退出后对操作系统使用的诉求&#xff0c;OpenAnolis龙蜥社区正式发布了Anolis OS。越来越多的CentOS客户期望能够迁移到Anolis OS上来。操作系统迁移是一个复杂工程&#xff0c;手工迁移技术要求高&#xff0c;操作复杂度强&#xff0c;需要耗费大…

《‌黑神话:‌悟空》‌游戏攻略‌

时光荏苒&#xff0c;岁月如梭&#xff0c;不知不觉已经来到了2024年的9月份了。 ‌突然想写一篇关于《‌黑神话&#xff1a;‌悟空》‌的游戏攻略‌。 在《‌黑神话&#xff1a;‌悟空》‌这款以中国古代名著《‌西游记》‌为背景的动作角色扮演游戏中&#xff0c;‌玩家将扮…

CSS学习13

CSS例子 学成网 需要使用的图片&#xff1a; 代码&#xff1a; <html><head><style>/*CSS初始化*/* { /*清除内外边框*/padding: 0;margin: 0;}ul {list-style: none; /*清除列表样式*/}.clearfix:before,.clearfix:after { /*清除浮动*/content: &qu…

OpenAI发布GPT-4o mini,3.5从此退出历史舞台?

随着OpenAI在2024年7月18日正式发布GPT-4o Mini&#xff0c;无疑在科技界引发了一场新的风暴。这一创新不仅标志着GPT-3.5模型正式退出历史舞台&#xff0c;更预示着人工智能在自然语言处理领域迈入了一个全新的时代。 之前速度最快的模型一直是GPT3.5&#xff0c;随着后来的GP…

我用 GPT 学占星

最近对占星赶兴趣&#xff0c;但是看到星盘中好多名词&#xff0c;不懂是什么意思&#xff1f;所以直接问 gpt &#xff0c; 发现回答的真的很棒&#x1f389; &#xff01; 假如我想知道各个状态的具体是根据什么数据来显示的&#xff1f; 分分钟解决了我的问题&#xff1b; 我…