1. day02 FastDFS

2. starter-canal依赖问题

https://github.com/chenqian56131/spring-boot-starter-canal

进入starter-canal 文件夹下

  1. mvn install:install-file “-DgroupId=com.xpand “-DartifactId=starter-canal “-Dversion=0.0.1-SNAPSHOT “-Dpackaging=jar “-Dfile=starter-canal-0.0.1-SNAPSHOT.jar

刷新pom文件

详情看https://blog.csdn.net/xiaopeng_thriller/article/details/104761800

3. 通用mapper自定义方法

通用mapper中的方法 无法进行多表查询 需要我们自定义sql语句 我们可以通过注解或mapper xml形式书写sql

dao层

  1. package com.changgou.goods.dao;
  2. import com.changgou.goods.pojo.Spec;
  3. import org.apache.ibatis.annotations.Param;
  4. import org.apache.ibatis.annotations.Select;
  5. import tk.mybatis.mapper.common.Mapper;
  6. import java.util.List;
  7. import java.util.Map;
  8. public interface SpecMapper extends Mapper<Spec> {
  9. @Select("SELECT `name`,`options` FROM tb_spec WHERE template_id IN(SELECT template_id FROM tb_category WHERE `name`=#{categoryName})")
  10. List<Map> findSpecListByCategoryName(@Param("categoryName") String categoryName);
  11. }

service层 新增加方法

  1. /**
  2. * 根据商品分类名称查询规格列表
  3. * @param categoryName
  4. * @return
  5. */
  6. public List<Map> findListByCategoryName(String categoryName);

ServiceImpl实现方法

  1. @Override
  2. public List<Map> findListByCategoryName(String categoryName) {
  3. List<Map> specList = specMapper.findListByCategoryName(categoryName);
  4. for(Map spec:specList){
  5. String[] options = ((String) spec.get("options")).split(",");//规格选项列表
  6. spec.put("options",options);
  7. }
  8. return specList;
  9. }

Controller新增方法

  1. /**
  2. * 根据商品分类名称查询规格列表
  3. * @param category
  4. * @return
  5. */
  6. @GetMapping("/category/{category}")
  7. public Result findListByCategoryName(@PathVariable String category){
  8. List<Map> specList = specService.findListByCategoryName(category);
  9. return new Result(true,StatusCode.OK,"",specList);
  10. }

4. 分布式文件存储 FastDFS

FastDFS是阿里的一个开源轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。

FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。

FastDFS 架构包括 Tracker serverStorage server。客户端请求 Tracker server 进行文件上传、下载,通过Tracker server 调度最终由 Storage server 完成文件上传和下载。

02. day02 FastDFS - 图1

4.1. 上传流程

02. day02 FastDFS - 图2

客户端上传文件后存储服务器将文件 ID 返回给客户端,此文件 ID 用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。

02. day02 FastDFS - 图3

组名:文件上传后所在的 storage 组名称,在文件上传成功后有storage 服务器返回,需要客户端自行保存。

虚拟磁盘路径:storage 配置的虚拟路径,与磁盘选项store_path*对应。如果配置了

store_path0 则是 M00,如果配置了 store_path1 则是 M01,以此类推。

数据两级目录:storage 服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据

文件。

文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储

服务器 IP 地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息。

4.2. FastDFS搭建

我们使用Docker搭建FastDFS的开发环境 拉取镜像

  1. docker pull morunchang/fastdfs
  2. #运行tracker
  3. docker run -d --name tracker --net=host morunchang/fastdfs sh tracker.sh
  4. #运行storage
  5. docker run -d --name storage --net=host -e TRACKER_IP=<your tracker server address>:22122 -e GROUP_NAME=<group name> morunchang/fastdfs sh storage.sh
  • 使用的网络模式是–net=host, 替换为你机器的Ip即可
  • 是组名,即storage的组
  • 如果想要增加新的storage服务器,再次运行该命令,注意更换 新组名

默认ip为192.168.200.128 如果vlan不是这个网关则需要以下操作

方法1.创新建一个storage

  1. docker run -d --name tracker --network=host morunchang/fastdfs sh tracker.sh
  2. docker run -d --name storage --net=host -e TRACKER_IP=192.168.130.128:22122 -e GROUP_NAME=group1 morunchang/fastdfs sh storage.sh

方法2.修改已有storage的环境变量

  1. docker inspect 容器
  2. cd /var/lib/docker/containers/ inspect查出的id
  3. vim config.v2.json

修改nginx的配置

  1. #进入storage的容器内部,修改nginx.conf
  2. docker exec -it storage /bin/bash
  3. vi /data/nginx/conf/nginx.conf
  4. #将server 80端口 改为 8080
  5. #添加以下内容
  6. location ~ /M00 {
  7. root /data/fast_data/data;
  8. ngx_fastdfs_module;
  9. }

重启容器

  1. exit
  2. docker restart storage

开机自启

  1. docker update --restart=always tracker
  2. docker update --restart=always storage

4.3. 文件存储微服务

创建文件管理微服务changgou_service_file,该工程主要用于实现文件上传以及文件删除等功能。该项目是changgou_service的子项目

  • pom文件
  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-web</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>net.oschina.zcx7878</groupId>
  8. <artifactId>fastdfs-client-java</artifactId>
  9. <version>1.27.0.0</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>com.changgou</groupId>
  13. <artifactId>changgou_common</artifactId>
  14. <version>1.0-SNAPSHOT</version>
  15. </dependency>
  16. </dependencies>
  • 在resources文件夹下创建fasfDFS的配置文件fdfs_client.conf
  1. connect_timeout = 60
  2. network_timeout = 60
  3. charset = UTF-8
  4. http.tracker_http_port = 8080
  5. tracker_server = 192.168.130.128:22122

connect_timeout:连接超时时间,单位为秒。

network_timeout:通信超时时间,单位为秒。发送或接收数据时。假设在超时时间后还不能发送或接收数据,则本次网络通信失败

charset: 字符集

http.tracker_http_port :.tracker的http端口

tracker_server: tracker服务器IP和端口设置

  • application.yml
  1. spring:
  2. servlet:
  3. multipart:
  4. max-file-size: 10MB # 单个文件大小
  5. max-request-size: 10MB # 设置总上传的数据大小
  6. server:
  7. port: 9008
  8. eureka:
  9. client:
  10. service-url:
  11. defaultZone: http://127.0.0.1:6868/eureka
  12. instance:
  13. prefer-ip-address: true
  14. feign:
  15. hystrix:
  16. enabled: true
  • 启动类
  1. package com.changgou.file;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
  5. @SpringBootApplication
  6. @EnableEurekaClient
  7. public class FileApplication {
  8. public static void main(String[] args) {
  9. SpringApplication.run(FileApplication.class,args);
  10. }
  11. }

4.4. 文件上传

4.4.1. 文件信息封装

文件上传一般都有文件的名字、文件的内容、文件的扩展名、文件的md5值、文件的作者等相关属性,我们可以创建一个对象封装这些属性

  1. package com.changgou.file.util;
  2. public class FastDFSFile {
  3. //文件名字
  4. private String name;
  5. //文件内容
  6. private byte[] content;
  7. //文件扩展名
  8. private String ext;
  9. //文件MD5摘要值
  10. private String md5;
  11. //文件创建作者
  12. private String author;
  13. public FastDFSFile(String name, byte[] content, String ext, String height,
  14. String width, String author) {
  15. super();
  16. this.name = name;
  17. this.content = content;
  18. this.ext = ext;
  19. this.author = author;
  20. }
  21. public FastDFSFile(String name, byte[] content, String ext) {
  22. super();
  23. this.name = name;
  24. this.content = content;
  25. this.ext = ext;
  26. }
  27. public String getName() {
  28. return name;
  29. }
  30. public void setName(String name) {
  31. this.name = name;
  32. }
  33. public byte[] getContent() {
  34. return content;
  35. }
  36. public void setContent(byte[] content) {
  37. this.content = content;
  38. }
  39. public String getExt() {
  40. return ext;
  41. }
  42. public void setExt(String ext) {
  43. this.ext = ext;
  44. }
  45. public String getMd5() {
  46. return md5;
  47. }
  48. public void setMd5(String md5) {
  49. this.md5 = md5;
  50. }
  51. public String getAuthor() {
  52. return author;
  53. }
  54. public void setAuthor(String author) {
  55. this.author = author;
  56. }
  57. }

4.4.2. 文件操作类

该类用于连接fastDFS 并执行文件的增删改查工具类

  1. package com.changgou.file.util;
  2. import org.csource.common.NameValuePair;
  3. import org.csource.fastdfs.*;
  4. import org.slf4j.LoggerFactory;
  5. import org.springframework.core.io.ClassPathResource;
  6. import java.io.ByteArrayInputStream;
  7. import java.io.IOException;
  8. import java.io.InputStream;
  9. public class FastDFSClient {
  10. private static org.slf4j.Logger logger = LoggerFactory.getLogger(FastDFSClient.class);
  11. /***
  12. * 初始化加载FastDFS的TrackerServer配置
  13. */
  14. static {
  15. try {
  16. String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();
  17. ClientGlobal.init(filePath);
  18. } catch (Exception e) {
  19. logger.error("FastDFS Client Init Fail!",e);
  20. }
  21. }
  22. /***
  23. * 文件上传
  24. * @param file
  25. * @return 返回一个字符串数组 1.文件的组名 2.文件的路径信息
  26. */
  27. public static String[] upload(FastDFSFile file) {
  28. //获取文件的作者
  29. NameValuePair[] meta_list = new NameValuePair[1];
  30. meta_list[0] = new NameValuePair("author", file.getAuthor());
  31. //接收返回数据
  32. String[] uploadResults = null;
  33. StorageClient storageClient=null;
  34. try {
  35. //创建StorageClient客户端对象
  36. storageClient = getTrackerClient();
  37. /***
  38. * 文件上传
  39. * 1)文件字节数组
  40. * 2)文件扩展名
  41. * 3)文件作者
  42. */
  43. uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
  44. } catch (Exception e) {
  45. logger.error("Exception when uploadind the file:" + file.getName(), e);
  46. }
  47. if (uploadResults == null && storageClient!=null) {
  48. logger.error("upload file fail, error code:" + storageClient.getErrorCode());
  49. }
  50. //获取组名
  51. String groupName = uploadResults[0];
  52. //获取文件存储路径
  53. String remoteFileName = uploadResults[1];
  54. return uploadResults;
  55. }
  56. /***
  57. * 获取文件信息
  58. * @param groupName:组名
  59. * @param remoteFileName:文件存储完整名
  60. * @return
  61. */
  62. public static FileInfo getFile(String groupName, String remoteFileName) {
  63. try {
  64. StorageClient storageClient = getTrackerClient();
  65. return storageClient.get_file_info(groupName, remoteFileName);
  66. } catch (Exception e) {
  67. logger.error("Exception: Get File from Fast DFS failed", e);
  68. }
  69. return null;
  70. }
  71. /***
  72. * 文件下载
  73. * @param groupName
  74. * @param remoteFileName
  75. * @return
  76. */
  77. public static InputStream downFile(String groupName, String remoteFileName) {
  78. try {
  79. //创建StorageClient
  80. StorageClient storageClient = getTrackerClient();
  81. //下载文件
  82. byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
  83. InputStream ins = new ByteArrayInputStream(fileByte);
  84. return ins;
  85. } catch (Exception e) {
  86. logger.error("Exception: Get File from Fast DFS failed", e);
  87. }
  88. return null;
  89. }
  90. /***
  91. * 文件删除
  92. * @param groupName
  93. * @param remoteFileName
  94. * @throws Exception
  95. */
  96. public static void deleteFile(String groupName, String remoteFileName)
  97. throws Exception {
  98. //创建StorageClient
  99. StorageClient storageClient = getTrackerClient();
  100. //删除文件
  101. int i = storageClient.delete_file(groupName, remoteFileName);
  102. }
  103. /***
  104. * 获取Storage组
  105. * @param groupName
  106. * @return
  107. * @throws IOException
  108. */
  109. public static StorageServer[] getStoreStorages(String groupName)
  110. throws IOException {
  111. //创建TrackerClient
  112. TrackerClient trackerClient = new TrackerClient();
  113. //获取TrackerServer
  114. TrackerServer trackerServer = trackerClient.getConnection();
  115. //获取Storage组
  116. return trackerClient.getStoreStorages(trackerServer, groupName);
  117. }
  118. /***
  119. * 获取Storage信息,IP和端口
  120. * @param groupName
  121. * @param remoteFileName
  122. * @return
  123. * @throws IOException
  124. */
  125. public static ServerInfo[] getFetchStorages(String groupName,
  126. String remoteFileName) throws IOException {
  127. TrackerClient trackerClient = new TrackerClient();
  128. TrackerServer trackerServer = trackerClient.getConnection();
  129. return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName);
  130. }
  131. /***
  132. * 获取Tracker服务地址
  133. * @return
  134. * @throws IOException
  135. */
  136. public static String getTrackerUrl() throws IOException {
  137. return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":"+ClientGlobal.getG_tracker_http_port()+"/";
  138. }
  139. /***
  140. * 获取Storage客户端
  141. * @return
  142. * @throws IOException
  143. */
  144. private static StorageClient getTrackerClient() throws IOException {
  145. TrackerServer trackerServer = getTrackerServer();
  146. StorageClient storageClient = new StorageClient(trackerServer, null);
  147. return storageClient;
  148. }
  149. /***
  150. * 获取Tracker
  151. * @return
  152. * @throws IOException
  153. */
  154. private static TrackerServer getTrackerServer() throws IOException {
  155. TrackerClient trackerClient = new TrackerClient();
  156. TrackerServer trackerServer = trackerClient.getConnection();
  157. return trackerServer;
  158. }
  159. }

4.4.3. 文件上传接口

  1. package com.changgou.file.controller;
  2. import com.changgou.entity.Result;
  3. import com.changgou.entity.StatusCode;
  4. import com.changgou.file.util.FastDFSClient;
  5. import com.changgou.file.util.FastDFSFile;
  6. import org.apache.commons.lang.StringUtils;
  7. import org.springframework.web.bind.annotation.PostMapping;
  8. import org.springframework.web.bind.annotation.RequestMapping;
  9. import org.springframework.web.bind.annotation.RestController;
  10. import org.springframework.web.multipart.MultipartFile;
  11. @RestController
  12. @RequestMapping("/file")
  13. public class FileController {
  14. @PostMapping("/upload")
  15. public Result uploadFile(MultipartFile file) {
  16. try {
  17. //判断文件是否已经存在
  18. if (file == null) {
  19. throw new RuntimeException("当前文件不存在");
  20. }
  21. //获取文件的完整名称
  22. String originalFilename = file.getOriginalFilename();
  23. if (StringUtils.isEmpty(originalFilename)) {
  24. throw new RuntimeException("当前文件不存在");
  25. }
  26. //获取文件的扩展名称
  27. String extName = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
  28. //获取文件内存
  29. byte[] content = file.getBytes();
  30. //创建文件上传的实体类
  31. FastDFSFile fastDFSFile = new FastDFSFile(originalFilename, content, extName);
  32. //调用上传工具类 进行文件上传 并接受返回字符串数组结果
  33. String[] uploadResult = FastDFSClient.upload(fastDFSFile);
  34. //封装返回结果
  35. String url = FastDFSClient.getTrackerUrl() + uploadResult[0] + "/" + uploadResult[1];
  36. return new Result(true, StatusCode.OK, "文件上传成功", url);
  37. } catch (Exception e) {
  38. e.printStackTrace();
  39. }
  40. return new Result(false, StatusCode.ERROR, "文件上传失败");
  41. }
  42. }

4.4.4. 使用postman测试

02. day02 FastDFS - 图4