Chiriri's blog Chiriri's blog
首页
  • Java

    • JavaSE
    • JavaEE
    • 设计模式
  • Python

    • Python
    • Python模块
    • 机器学习
  • Golang

    • Golang
    • gRPC
  • 服务器

    • Linux
    • MySQL
    • NoSQL
    • Kubernetes
  • 项目

    • 传智健康
    • 畅购商城
  • Hadoop生态

    • Hadoop
    • Zookeeper
    • Hive
    • Flume
    • Kafka
    • Azkaban
    • Hbase
    • Scala
    • Spark
    • Flink
  • 大数据项目

    • 离线数仓
  • 青训营

    • 第四届青训营
  • HTML

    • HTML
    • JavaScript
  • Vue

    • Vue2
    • TypeScript
    • Vue3
    • Uni-APP
  • 数据结构与算法
  • C语言
  • 考研数据结构
  • 计算机组成原理
  • 计算机操作系统
  • Java基础

    • Java基础
    • Java集合
    • JUC
    • JVM
  • 框架

    • Spring
    • Dubbo
    • Spring Cloud
  • 数据库

    • MySQL
    • Redis
    • Elasticesearch
  • 消息队列

    • RabbitMQ
    • RocketMQ
  • 408

    • 计算机网络
    • 操作系统
    • 算法
  • 分类
  • 标签
  • 归档
  • 导航站
GitHub (opens new window)

Iekr

苦逼后端开发
首页
  • Java

    • JavaSE
    • JavaEE
    • 设计模式
  • Python

    • Python
    • Python模块
    • 机器学习
  • Golang

    • Golang
    • gRPC
  • 服务器

    • Linux
    • MySQL
    • NoSQL
    • Kubernetes
  • 项目

    • 传智健康
    • 畅购商城
  • Hadoop生态

    • Hadoop
    • Zookeeper
    • Hive
    • Flume
    • Kafka
    • Azkaban
    • Hbase
    • Scala
    • Spark
    • Flink
  • 大数据项目

    • 离线数仓
  • 青训营

    • 第四届青训营
  • HTML

    • HTML
    • JavaScript
  • Vue

    • Vue2
    • TypeScript
    • Vue3
    • Uni-APP
  • 数据结构与算法
  • C语言
  • 考研数据结构
  • 计算机组成原理
  • 计算机操作系统
  • Java基础

    • Java基础
    • Java集合
    • JUC
    • JVM
  • 框架

    • Spring
    • Dubbo
    • Spring Cloud
  • 数据库

    • MySQL
    • Redis
    • Elasticesearch
  • 消息队列

    • RabbitMQ
    • RocketMQ
  • 408

    • 计算机网络
    • 操作系统
    • 算法
  • 分类
  • 标签
  • 归档
  • 导航站
GitHub (opens new window)
  • JavaSE

  • JavaEE

  • Linux

  • MySQL

  • NoSQL

  • Python

  • Python模块

  • 机器学习

  • 设计模式

  • 传智健康

  • 畅购商城

    • Day01 项目搭建
    • Day02 FastDFS
      • starter-canal依赖问题
      • 通用mapper自定义方法
      • 分布式文件存储 FastDFS
        • 上传流程
        • FastDFS搭建
        • 文件存储微服务
        • 文件上传
        • 文件信息封装
        • 文件操作类
        • 文件上传接口
        • 使用postman测试
    • Day03 微服务鉴权
    • Day04 新增和修改商品
    • Day05 广告缓存
    • Day06 监听数据库更新广告缓存
    • Day07 ES搜索
    • Day08 Thymeleaf
    • Day09 Oauth2
    • Day10 购物车渲染
    • Day11 订单结算
    • Day12 分布式事务解决方案
    • Day13 微信支付
    • Day14 订单处理
    • Day15 秒杀前端
    • Day16 秒杀后端
  • 博客项目

  • JVM

  • JUC

  • Golang

  • Kubernetes

  • 硅谷课堂

  • C

  • 源码

  • 神领物流

  • RocketMQ

  • 短链平台

  • 后端
  • 畅购商城
Iekr
2021-11-14
目录

Day02 FastDFS

# Day02 FastDFS

# starter-canal 依赖问题

https://github.com/chenqian56131/spring-boot (opens new window)-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”
1

刷新 pom 文件

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

# 通用 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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

service 层 新增加方法

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

ServiceImpl 实现方法

@Override
public 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;
}
1
2
3
4
5
6
7
8
9

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);
}
1
2
3
4
5
6
7
8
9
10

# 分布式文件存储 FastDFS

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

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

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

1559117928459

# 上传流程

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

1559118013272

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

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

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

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

文件。

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

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

# FastDFS 搭建

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

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

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

方法 1. 创新建一个 storage

docker run -d --name tracker --network=host morunchang/fastdfs sh tracker.sh

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

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

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

修改 nginx 的配置

#进入storage的容器内部,修改nginx.conf
docker exec -it storage  /bin/bash

vi /data/nginx/conf/nginx.conf
#将server 80端口 改为 8080
#添加以下内容
location ~ /M00 {
     root /data/fast_data/data;
     ngx_fastdfs_module;
}
1
2
3
4
5
6
7
8
9
10

重启容器

exit
docker restart storage
1
2

开机自启

docker update --restart=always tracker
docker update --restart=always storage

1
2
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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 在 resources 文件夹下创建 fasfDFS 的配置文件 fdfs_client.conf
connect_timeout = 60
network_timeout = 60
charset = UTF-8
http.tracker_http_port = 8080
tracker_server = 192.168.130.128:22122
1
2
3
4
5

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: 9008
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:6868/eureka
  instance:
    prefer-ip-address: true
feign:
  hystrix:
    enabled: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 启动类
package com.changgou.file;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class FileApplication {
    public static void main(String[] args) {
        SpringApplication.run(FileApplication.class,args);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 文件上传

# 文件信息封装

文件上传一般都有文件的名字、文件的内容、文件的扩展名、文件的 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;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

# 文件操作类

该类用于连接 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 {
            //创建StorageClient
            StorageClient 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 {
        //创建StorageClient
        StorageClient storageClient = getTrackerClient();

        //删除文件
        int i = storageClient.delete_file(groupName, remoteFileName);
    }

    /***
     * 获取Storage组
     * @param groupName
     * @return
     * @throws IOException
     */
    public static StorageServer[] getStoreStorages(String groupName)
            throws IOException {
        //创建TrackerClient
        TrackerClient trackerClient = new TrackerClient();
        //获取TrackerServer
        TrackerServer 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;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178

# 文件上传接口

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, "文件上传失败");
    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

# 使用 postman 测试

image-20211114160530636

编辑 (opens new window)
上次更新: 2023/12/06, 01:31:48
Day01 项目搭建
Day03 微服务鉴权

← Day01 项目搭建 Day03 微服务鉴权→

最近更新
01
k8s
06-06
02
进程与线程
03-04
03
计算机操作系统概述
02-26
更多文章>
Theme by Vdoing | Copyright © 2022-2025 Iekr | Blog
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式