介绍

FastDFS 是一个由 C 语言实现的开源轻量级分布式文件系统

分布式文件系统(Distributed File System)是指文件系统管理的物理存储资源不一定直接连接在本地节点上,而是通过计算机网络与节点相连。通俗来讲:传统文件系统管理的文件就存储在本机。分布式文件系统管理的文件存储在很多机器,这些机器通过网络连接,要被统一管理。无论是上传或者访问文件,都需要通过管理中心来访问。

FastDFS 上传的流程

  1. Client通过 Tracker server 查找可用的 Storage server
  2. Tracker serverClient 返回可用的 Storage server 的 IP 地址和端口号。
  3. Client 直接通过 Tracker server 返回的 IP 地址和端口与其中一台 Storage server 建立连接并进行文件上传。
  4. 上传完成,Storage server 返回 Client 一个文件ID,文件上传结束。

FastDFS 下载的流程

  1. Client 通过 Tracker server 查找要下载文件所在的的 Storage Server
  2. Tracker serverClient 返回包含指定文件的某个 Storage server的 IP 地址和端口号
  3. Client 直接通过 Tracker server 返回的 IP 地址和端口与其中一台 Storage server 建立连接并指定要下载文件
  4. 下载文件成功

创建 fastdfs

使用 docker 安装

拉取镜像

1
docker pull morunchang/fastdfs

首先创建并启动tracker容器

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

再创建并启动storage容器

1
docker run -d --name storage --net=host -e TRACKER_IP=192.168.200.128:22122 -e GROUP_NAME=group1 morunchang/fastdfs sh storage.sh
  • 使用的网络模式是–net=host,此时会将宿主机的网络应用于容器,链接容器就可以直接使用宿主机的 IP(ip 需要根据自己的实际情况调整!
  • sh tracker.sh运行tracker.sh脚本文件
  • group1 是组名,即 storage 的组
  • 如果想要增加新的 storage 服务器,再次运行该命令,只需要注意更换新组名

配置 Nginx(可选)

Nginx 在这里主要提供对 FastDFS 图片访问的支持,storage 容器中已经集成了 Nginx

Nginx 集成了 FastDFS,可以通过它的 ngx_fastdfs_module 模块访问 Tracker 获取图片所存储的 Storage 信息,然后访问 Storage信息获取图片信息。

进入 storage 的容器内部,修改 nginx.conf

1
docker exec -it storage  /bin/bash

进入后

1
vi /etc/nginx/conf/nginx.conf

添加以下内容

访问图片的时候,浏览器通常都会对图片进行缓存

如果需要禁止缓存,可以设置 nginx 配置添加禁止缓存(主要用于防止用户删除某张图片后因为本地缓存原因还能访问)

1
add_header Cache-Control no-store;

退出并重启 storage 容器

1
2
exit
docker restart storage

java 客户端

引入依赖

1
2
3
4
5
6
<!--fastdfs-->
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
<version>1.27.2</version>
</dependency>

配置类

1
2
3
4
5
6
7
8
9
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;

@Configuration
@Import(FastDfsClient.class)
@PropertySource("classpath:fast_dfs.properties")
public class FastDfsConfig {
}

配置文件fast_dfs.properties

1
2
3
4
5
6
#socket连接超时时长
fdfs.soTimeout=1500
#连接tracker服务器超时时长
fdfs.connectTimeout=600
#tracker地址:服务器地址(上面创建容器时自己配置的ip)+端口号
fdfs.trackerList=192.168.200.128:22122

工具类 FastDfsClient

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
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.domain.proto.storage.DownloadCallback;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
* 操作fastdsf的工具类
*/
@Component
public class FastDfsClient {

@Autowired
private FastFileStorageClient fastFileStorageClient;

/**
* 文件上传
*/
public String upload(MultipartFile file) throws Exception {
//文件数据流
StorePath storePath = fastFileStorageClient.uploadFile(file.getInputStream(),
//文件大小
file.getSize(),
//文件拓展名
StringUtils.getFilenameExtension(file.getOriginalFilename()),
//附加参数
null);
//返回全量路径
return storePath.getFullPath();
}

/**
* 文件下载
*
* @return
*/
public byte[] download(String groupName, String path) throws Exception {
//下载文件
InputStream inputStream = fastFileStorageClient.downloadFile(groupName, path, new DownloadCallback<InputStream>() {
//下载发生异常返回异常数据流
@Override
public InputStream recv(InputStream ins) throws IOException {
return ins;
}
});
//声明输出流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//声明缓冲区
byte[] buffer = new byte[1024];
//定义每次读取的数据长度
int lengh = 0;
//输入流转输出流
while ((lengh = inputStream.read(buffer)) != -1) {
//输出流写入
byteArrayOutputStream.write(buffer, 0, lengh);
}
//返回字节码
return byteArrayOutputStream.toByteArray();
}

/**
* 文件删除
*/
public void delete(String groupName, String path) {
fastFileStorageClient.deleteFile(groupName, path);
}
}

手动实现文件上传

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
/**
* 从自己写的application.yml配置文件中获取fastDFS的ip:port
*/
@Value("${fileServer.url}")
private String imageUrl;

@PostMapping(value = "/fileUpload")
public String fileUpload(@RequestParam("file") MultipartFile file) throws Exception {
//加载配置文件
ClassPathResource classPathResource = new ClassPathResource("tracker.conf");
//初始化fastdfs对象
ClientGlobal.init(classPathResource.getPath());
//声明trackerClient
TrackerClient trackerClient = new TrackerClient();
//获取服务连接信息
TrackerServer connection = trackerClient.getConnection();
//初始化storageClient
StorageClient storageClient = new StorageClient(connection, null);
//获取文件拓展名
String filenameExtension = StringUtils.getFilenameExtension(file.getOriginalFilename());
//上传文件
// 1.文件字节码 2.文件拓展名 3.附加参数
// 返回结果为数组: 0:组名 1:文件的完全路径名+文件名
String[] uploadFile = storageClient.upload_file(file.getBytes(),
filenameExtension,
null);
//拼接图片访问的url地址
String url = imageUrl + uploadFile[0] + "/" + uploadFile[1];
//返回图片全量访问路径
return url;