1. day02 FastDFS
2. starter-canal依赖问题
https://github.com/chenqian56131/spring-boot-starter-canal
进入starter-canal 文件夹下
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层
package com.changgou.goods.dao;import com.changgou.goods.pojo.Spec;import org.apache.ibatis.annotations.Param;import org.apache.ibatis.annotations.Select;import tk.mybatis.mapper.common.Mapper;import java.util.List;import java.util.Map;public interface SpecMapper extends Mapper<Spec> {@Select("SELECT `name`,`options` FROM tb_spec WHERE template_id IN(SELECT template_id FROM tb_category WHERE `name`=#{categoryName})")List<Map> findSpecListByCategoryName(@Param("categoryName") String categoryName);}
service层 新增加方法
/*** 根据商品分类名称查询规格列表* @param categoryName* @return*/public List<Map> findListByCategoryName(String categoryName);
ServiceImpl实现方法
@Overridepublic List<Map> findListByCategoryName(String categoryName) {List<Map> specList = specMapper.findListByCategoryName(categoryName);for(Map spec:specList){String[] options = ((String) spec.get("options")).split(",");//规格选项列表spec.put("options",options);}return specList;}
Controller新增方法
/*** 根据商品分类名称查询规格列表* @param category* @return*/@GetMapping("/category/{category}")public Result findListByCategoryName(@PathVariable String category){List<Map> specList = specService.findListByCategoryName(category);return new Result(true,StatusCode.OK,"",specList);}
4. 分布式文件存储 FastDFS
FastDFS是阿里的一个开源轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。
FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
FastDFS 架构包括 Tracker server 和 Storage server。客户端请求 Tracker server 进行文件上传、下载,通过Tracker server 调度最终由 Storage server 完成文件上传和下载。

4.1. 上传流程

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

组名:文件上传后所在的 storage 组名称,在文件上传成功后有storage 服务器返回,需要客户端自行保存。
虚拟磁盘路径:storage 配置的虚拟路径,与磁盘选项store_path*对应。如果配置了
store_path0 则是 M00,如果配置了 store_path1 则是 M01,以此类推。
数据两级目录:storage 服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据
文件。
文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储
服务器 IP 地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息。
4.2. FastDFS搭建
我们使用Docker搭建FastDFS的开发环境 拉取镜像
docker pull morunchang/fastdfs#运行trackerdocker run -d --name tracker --net=host morunchang/fastdfs sh tracker.sh#运行storagedocker 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
docker run -d --name tracker --network=host morunchang/fastdfs sh tracker.shdocker 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的环境变量
docker inspect 容器cd /var/lib/docker/containers/ inspect查出的idvim config.v2.json
修改nginx的配置
#进入storage的容器内部,修改nginx.confdocker exec -it storage /bin/bashvi /data/nginx/conf/nginx.conf#将server 80端口 改为 8080#添加以下内容location ~ /M00 {root /data/fast_data/data;ngx_fastdfs_module;}
重启容器
exitdocker restart storage
开机自启
docker update --restart=always trackerdocker update --restart=always storage
4.3. 文件存储微服务
创建文件管理微服务changgou_service_file,该工程主要用于实现文件上传以及文件删除等功能。该项目是changgou_service的子项目
- pom文件
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>net.oschina.zcx7878</groupId><artifactId>fastdfs-client-java</artifactId><version>1.27.0.0</version></dependency><dependency><groupId>com.changgou</groupId><artifactId>changgou_common</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>
- 在resources文件夹下创建fasfDFS的配置文件fdfs_client.conf
connect_timeout = 60network_timeout = 60charset = UTF-8http.tracker_http_port = 8080tracker_server = 192.168.130.128:22122
connect_timeout:连接超时时间,单位为秒。
network_timeout:通信超时时间,单位为秒。发送或接收数据时。假设在超时时间后还不能发送或接收数据,则本次网络通信失败
charset: 字符集
http.tracker_http_port :.tracker的http端口
tracker_server: tracker服务器IP和端口设置
- application.yml
spring:servlet:multipart:max-file-size: 10MB # 单个文件大小max-request-size: 10MB # 设置总上传的数据大小server:port: 9008eureka:client:service-url:defaultZone: http://127.0.0.1:6868/eurekainstance:prefer-ip-address: truefeign:hystrix:enabled: true
- 启动类
package com.changgou.file;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;@SpringBootApplication@EnableEurekaClientpublic class FileApplication {public static void main(String[] args) {SpringApplication.run(FileApplication.class,args);}}
4.4. 文件上传
4.4.1. 文件信息封装
文件上传一般都有文件的名字、文件的内容、文件的扩展名、文件的md5值、文件的作者等相关属性,我们可以创建一个对象封装这些属性
package com.changgou.file.util;public class FastDFSFile {//文件名字private String name;//文件内容private byte[] content;//文件扩展名private String ext;//文件MD5摘要值private String md5;//文件创建作者private String author;public FastDFSFile(String name, byte[] content, String ext, String height,String width, String author) {super();this.name = name;this.content = content;this.ext = ext;this.author = author;}public FastDFSFile(String name, byte[] content, String ext) {super();this.name = name;this.content = content;this.ext = ext;}public String getName() {return name;}public void setName(String name) {this.name = name;}public byte[] getContent() {return content;}public void setContent(byte[] content) {this.content = content;}public String getExt() {return ext;}public void setExt(String ext) {this.ext = ext;}public String getMd5() {return md5;}public void setMd5(String md5) {this.md5 = md5;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}}
4.4.2. 文件操作类
该类用于连接fastDFS 并执行文件的增删改查工具类
package com.changgou.file.util;import org.csource.common.NameValuePair;import org.csource.fastdfs.*;import org.slf4j.LoggerFactory;import org.springframework.core.io.ClassPathResource;import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStream;public class FastDFSClient {private static org.slf4j.Logger logger = LoggerFactory.getLogger(FastDFSClient.class);/**** 初始化加载FastDFS的TrackerServer配置*/static {try {String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();ClientGlobal.init(filePath);} catch (Exception e) {logger.error("FastDFS Client Init Fail!",e);}}/**** 文件上传* @param file* @return 返回一个字符串数组 1.文件的组名 2.文件的路径信息*/public static String[] upload(FastDFSFile file) {//获取文件的作者NameValuePair[] meta_list = new NameValuePair[1];meta_list[0] = new NameValuePair("author", file.getAuthor());//接收返回数据String[] uploadResults = null;StorageClient storageClient=null;try {//创建StorageClient客户端对象storageClient = getTrackerClient();/**** 文件上传* 1)文件字节数组* 2)文件扩展名* 3)文件作者*/uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);} catch (Exception e) {logger.error("Exception when uploadind the file:" + file.getName(), e);}if (uploadResults == null && storageClient!=null) {logger.error("upload file fail, error code:" + storageClient.getErrorCode());}//获取组名String groupName = uploadResults[0];//获取文件存储路径String remoteFileName = uploadResults[1];return uploadResults;}/**** 获取文件信息* @param groupName:组名* @param remoteFileName:文件存储完整名* @return*/public static FileInfo getFile(String groupName, String remoteFileName) {try {StorageClient storageClient = getTrackerClient();return storageClient.get_file_info(groupName, remoteFileName);} catch (Exception e) {logger.error("Exception: Get File from Fast DFS failed", e);}return null;}/**** 文件下载* @param groupName* @param remoteFileName* @return*/public static InputStream downFile(String groupName, String remoteFileName) {try {//创建StorageClientStorageClient storageClient = getTrackerClient();//下载文件byte[] fileByte = storageClient.download_file(groupName, remoteFileName);InputStream ins = new ByteArrayInputStream(fileByte);return ins;} catch (Exception e) {logger.error("Exception: Get File from Fast DFS failed", e);}return null;}/**** 文件删除* @param groupName* @param remoteFileName* @throws Exception*/public static void deleteFile(String groupName, String remoteFileName)throws Exception {//创建StorageClientStorageClient storageClient = getTrackerClient();//删除文件int i = storageClient.delete_file(groupName, remoteFileName);}/**** 获取Storage组* @param groupName* @return* @throws IOException*/public static StorageServer[] getStoreStorages(String groupName)throws IOException {//创建TrackerClientTrackerClient trackerClient = new TrackerClient();//获取TrackerServerTrackerServer trackerServer = trackerClient.getConnection();//获取Storage组return trackerClient.getStoreStorages(trackerServer, groupName);}/**** 获取Storage信息,IP和端口* @param groupName* @param remoteFileName* @return* @throws IOException*/public static ServerInfo[] getFetchStorages(String groupName,String remoteFileName) throws IOException {TrackerClient trackerClient = new TrackerClient();TrackerServer trackerServer = trackerClient.getConnection();return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName);}/**** 获取Tracker服务地址* @return* @throws IOException*/public static String getTrackerUrl() throws IOException {return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":"+ClientGlobal.getG_tracker_http_port()+"/";}/**** 获取Storage客户端* @return* @throws IOException*/private static StorageClient getTrackerClient() throws IOException {TrackerServer trackerServer = getTrackerServer();StorageClient storageClient = new StorageClient(trackerServer, null);return storageClient;}/**** 获取Tracker* @return* @throws IOException*/private static TrackerServer getTrackerServer() throws IOException {TrackerClient trackerClient = new TrackerClient();TrackerServer trackerServer = trackerClient.getConnection();return trackerServer;}}
4.4.3. 文件上传接口
package com.changgou.file.controller;import com.changgou.entity.Result;import com.changgou.entity.StatusCode;import com.changgou.file.util.FastDFSClient;import com.changgou.file.util.FastDFSFile;import org.apache.commons.lang.StringUtils;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.multipart.MultipartFile;@RestController@RequestMapping("/file")public class FileController {@PostMapping("/upload")public Result uploadFile(MultipartFile file) {try {//判断文件是否已经存在if (file == null) {throw new RuntimeException("当前文件不存在");}//获取文件的完整名称String originalFilename = file.getOriginalFilename();if (StringUtils.isEmpty(originalFilename)) {throw new RuntimeException("当前文件不存在");}//获取文件的扩展名称String extName = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);//获取文件内存byte[] content = file.getBytes();//创建文件上传的实体类FastDFSFile fastDFSFile = new FastDFSFile(originalFilename, content, extName);//调用上传工具类 进行文件上传 并接受返回字符串数组结果String[] uploadResult = FastDFSClient.upload(fastDFSFile);//封装返回结果String url = FastDFSClient.getTrackerUrl() + uploadResult[0] + "/" + uploadResult[1];return new Result(true, StatusCode.OK, "文件上传成功", url);} catch (Exception e) {e.printStackTrace();}return new Result(false, StatusCode.ERROR, "文件上传失败");}}
4.4.4. 使用postman测试

