FastDFS是一个开源的分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。FastDFS服务端有两个角色:跟踪器(tracker)和存储节点(storage)。跟踪器主要做调度工作,在访问上起负载均衡的作用。
1、拉取 FastDFS 镜像
docker pull morunchang/fastdfs
2、启动容器
1)、运行跟踪器 Tracker
docker run -d --name tracker --net=host morunchang/fastdfs sh tracker.sh
2)、运行存储节点 Storage
docker run -d --name storage --net=host -e TRACKER_IP=192.168.37.137:22122 -e GROUP_NAME=group1 morunchang/fastdfs sh storage.sh
使用的网络模式是–net=host, 192.168.37.137是宿主机的IPgroup1是组名,即storage的组如果想要增加新的storage服务器,再次运行该命令,注意更换 新组名如果想要挂载到本机目录,就在命令里面添加-v /data/fastdfs/storage:/data/fast_data
3、需要配置访问代理 Nginx
因为在 FastDFS 镜像中有自带的 Nginx 服务器,所以我们只需要进入容器配置下 Nginx 服务器即可,注意我们需要进入的是存储节点 Storage,具体的 Shell 命令如下:
docker exec -it storage /bin/bashvi /etc/nginx/conf/nginx.conf// 配置一个访问规则location ~ /M00 {root /data/fast_data/data;ngx_fastdfs_module;}// 在 nginx.conf 中配置禁止使用缓存add_header Cache-Control no-store;// 需要重新启动容器docker restart storage
搭建好之后,存储的文件的路径大概是 http://192.168.37.137:8080/group1/M00/00/00/rBUABl-CwNuAeRPyAAFwqWytcEA860.png 这个样子,端口 8080 实际上是 Storage 中的 Nginx 服务器的监听端口,如果需要改,去修改该配置文件即可。
文末彩蛋
提供后端配置 FastDFS 的工具类和配置
1、引入 POM 依赖
<dependency><groupId>net.oschina.zcx7878</groupId><artifactId>fastdfs-client-java</artifactId><version>1.27.0.0</version></dependency>
2、配置文件 fdfs_client.conf
connect_timeout=60network_timeout=60charset=UTF-8http.tracker_http_port=8080tracker_server=192.168.48.132:22122
3、自定义 FastDFS 文件类
import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.io.Serializable;/*** @author qiukangming* @version 1.0* @description* @since /11/23 22:18*/@Data@AllArgsConstructor@NoArgsConstructorpublic class FastDFSFile implements Serializable {//文件名字private String name;//文件内容private byte[] content;//文件扩展名private String ext;//文件MD5摘要值private String md5;//文件创建作者private String author;}
4、自定义 FastDFS 文件操作工具类
import mon.NameValuePair;import org.csource.fastdfs.ClientGlobal;import org.csource.fastdfs.FileInfo;import org.csource.fastdfs.ServerInfo;import org.csource.fastdfs.StorageClient;import org.csource.fastdfs.StorageServer;import org.csource.fastdfs.TrackerClient;import org.csource.fastdfs.TrackerServer;import org.springframework.core.io.ClassPathResource;import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStream;/*** @author qiukangming* @version 1.0* @description FastDFS服务器操作类* @since /11/24 11:24*/public class FastDFSClient {/** 初始化tracker信息*/static {try {//获取tracker的配置文件fdfs_client.conf的位置String filePath = new ClassPathResource("fdfs_client.conf").getPath();//加载tracker配置信息ClientGlobal.init(filePath);} catch (Exception e) {e.printStackTrace();}}/***** 文件上传* * @param file : 要上传的文件信息封装->FastDFSFile* @return String[]*1:文件上传所存储的组名*2:文件存储路径*/public static String[] upload(FastDFSFile file) {//获取文件作者NameValuePair[] meta_list = new NameValuePair[1];meta_list[0] = new NameValuePair(file.getAuthor());/** 文件上传后的返回值* uploadResults[0]:文件上传所存储的组名,例如:group1* uploadResults[1]:文件存储路径,例如:M00/00/00/wKjThF0DBzaAP23MAAXz2mMp9oM26.jpeg*/String[] uploadResults = null;try {//获取StorageClient对象StorageClient storageClient = getStorageClient();//执行文件上传uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);} catch (Exception e) {e.printStackTrace();}return uploadResults;}/**** 获取文件信息* * @param groupName:组名* @param remoteFileName:文件存储完整名*/public static FileInfo getFile(String groupName, String remoteFileName) {try {//获取StorageClient对象StorageClient storageClient = getStorageClient();//获取文件信息return storageClient.get_file_info(groupName, remoteFileName);} catch (Exception e) {e.printStackTrace();}return null;}/**** 文件下载* * @param groupName:组名* @param remoteFileName:文件存储完整名* @return InputStream*/public static InputStream downFile(String groupName, String remoteFileName) {try {//获取StorageClientStorageClient storageClient = getStorageClient();//通过StorageClient下载文件byte[] fileByte = storageClient.download_file(groupName, remoteFileName);//将字节数组转换成字节输入流return new ByteArrayInputStream(fileByte);} catch (Exception e) {e.printStackTrace();}return null;}/**** 文件删除实现* * @param groupName:组名* @param remoteFileName:文件存储完整名*/public static void deleteFile(String groupName,String remoteFileName) {try {//获取StorageClientStorageClient storageClient = getStorageClient();//通过StorageClient删除文件storageClient.delete_file(groupName, remoteFileName);} catch (Exception e) {e.printStackTrace();}}/**** 获取组信息* * @param groupName :组名*/public static StorageServer getStorages(String groupName) {try {//创建TrackerClient对象TrackerClient trackerClient = new TrackerClient();//通过TrackerClient获取TrackerServer对象TrackerServer trackerServer = trackerClient.getConnection();//通过trackerClient获取Storage组信息return trackerClient.getStoreStorage(trackerServer, groupName);} catch (Exception e) {e.printStackTrace();}return null;}/**** 根据文件组名和文件存储路径获取Storage服务的IP、端口信息* * @param groupName :组名* @param remoteFileName :文件存储完整名*/public static ServerInfo[] getServerInfo(String groupName, String remoteFileName) {try {//创建TrackerClient对象TrackerClient trackerClient = new TrackerClient();//通过TrackerClient获取TrackerServer对象TrackerServer trackerServer = trackerClient.getConnection();//获取服务信息return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName);} catch (Exception e) {e.printStackTrace();}return null;}/*** 获取Tracker服务地址*/public static String getTrackerUrl() {try {//创建TrackerClient对象TrackerClient trackerClient = new TrackerClient();//通过TrackerClient获取TrackerServer对象TrackerServer trackerServer = trackerClient.getConnection();//获取Tracker地址return "http://" + trackerServer.getInetSocketAddress().getHostString() + ":" + ClientGlobal.getG_tracker_http_port();} catch (IOException e) {e.printStackTrace();}return null;}/*** 获取TrackerServer*/public static TrackerServer getTrackerServer() throws Exception {//创建TrackerClient对象TrackerClient trackerClient = new TrackerClient();//通过TrackerClient获取TrackerServer对象return trackerClient.getConnection();}/*** 获取StorageClient* * @return StorageClient* @throws Exception e*/public static StorageClient getStorageClient() throws Exception {//获取TrackerServerTrackerServer trackerServer = getTrackerServer();//通过TrackerServer创建StorageClientreturn new StorageClient(trackerServer, null);}}
5、文件上传接口
import mon.utils.Result;import mon.utils.StatusCode;import com.qkm.file.dfs.FastDFSFile;import com.qkm.file.utils.FastDFSClient;import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.CrossOrigin;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.multipart.MultipartFile;/*** @author qiukangming* @version 1.0* @description* @since /11/24 11:18*/@RestController@CrossOrigin@RequestMapping("/file")public class FileController {@PostMapping("/upload")public Result<Object> upload(@RequestParam("file") MultipartFile multipartFile) throws Exception {FastDFSFile fastDFSFile = new FastDFSFile(multipartFile.getOriginalFilename(),multipartFile.getBytes(), StringUtils.getFilenameExtension(multipartFile.getOriginalFilename()), null, null);//文件上传String[] uploads = FastDFSClient.upload(fastDFSFile);//组装文件上传地址String url = FastDFSClient.getTrackerUrl()+"/"+uploads[0]+"/"+ uploads[1];return new Result<>(true, StatusCode.OK, "文件上传成功", url);}}