FastDFS是什么?
FastDFS是一个 使用c 语言编写的一款开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。
FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
FastDFS 简介
Fast DFS系统有三个角色:跟踪服务器(Tracker Server)、存储服务器(Storage Server)和客户端(Client)。client请求Tracker server 进行文件上传、下载,通过Tracker server调度最终由Storage server完成文件上传和下载,在底层存储上通过逻辑的分组概念,使得通过在同组内配置多个Storage,从而实现软RAID10。
FastDFS充分考虑了冗余备份、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
Tracker server:跟踪服务器,主要做调度工作,起到均衡的作用;负责管理所有的Storage server和group,每个storage在启动后会连接Tracker,告知自己所属group等信息,并保持周期性心跳。tracker上的元信息都是由storage汇报的信息生成的,本身不需要持久化任何数据,这样使得tracker非常容易扩展,直接增加tracker机器即可扩展为tracker cluster来服务,cluster里每个tracker之间是完全对等的,所有的tracker都接受stroage的心跳信息,生成元数据信息来提供读写服务,tracker根据storage的心跳信息,建立group==>[storage server list]的映射表。
Storage server:存储服务器,主要提供容量和备份服务;以group为单位,每个group内部可以有多台storage server,数据互为备份。客户端上传的文件最终存储在storage服务器上,Storage server没有实现自己的文件系统,而是利用操作系统的文件系统来管理文件,可以将storage称为存储服务器。storage可配置多个数据存储目录,比如有10块磁盘,分别挂载在/data/disk1-/data/disk10,则可将这10个目录都配置为storage的数据存储目录。
Client:客户端,上传下载数据的服务器,也就是我们自己的项目所部署在的服务器。FastDFS向使用者提供基本文件访问接口,比如upload、download、append、delete等,以客户端库的方式提供给用户使用
跟踪器和存储节点都可以由一台多台服务器构成。跟踪器和存储节点中的服务器均可以随时增加或下线而不会影响线上服务。其中跟踪器中的所有服务器都是对等的,可以根据服务器的压力情况随时增加或减少。
FastDFS 存储策略
为了支持大容量,存储节点(服务器)采用了分卷(或分组)的组织方式。存储系统由一个或多个卷组成,卷与卷之间的文件是相互独立的,所有卷 的文件容量累加就是整个存储系统中的文件容量。一个卷可以由一台或多台存储服务器组成,一个卷下的存储服务器中的文件都是相同的,卷中的多台存储服务器起 到了冗余备份和负载均衡的作用。
在卷中增加服务器时,同步已有的文件由系统自动完成,同步完成后,系统自动将新增服务器切换到线上提供服务。
当存储空间不足或即将耗尽时,可以动态添加卷。只需要增加一台或多台服务器,并将它们配置为一个新的卷,这样就扩大了存储系统的容量。
FastDFS中的文件标识分为两个部分:卷名和文件名,二者缺一不可。
上传文件交互过程:
- client询问tracker上传到的storage,不需要附加参数;
- tracker返回一台可用的storage;
- client直接和storage通讯完成文件上传。
下载文件交互过程:
- client询问tracker下载文件的storage,参数为文件标识(卷名和文件名);
- tracker返回一台可用的storage;
- client直接和storage通讯完成文件下载。
需要说明的是,client为使用FastDFS服务的调用方,client也应该是一台服务器,它对tracker和storage的调用均为服务器间的调用。
Docker安装FastDFS
查看fastdfs版本 docker search fastdfs
[root@bogon ~]# docker search fastdfs
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
season/fastdfs FastDFS 74
ygqygq2/fastdfs-nginx 整合了nginx的fastdfs 27 [OK]
luhuiguo/fastdfs FastDFS is an open source high performance d… 25 [OK]
morunchang/fastdfs A FastDFS image 20
qbanxiaoli/fastdfs FastDFS+FastDHT(单机+集群版) 12 [OK]
delron/fastdfs 12
moocu/fastdfs fastdfs5.11 9
ecarpo/fastdfs-storage 4
lionheart/fastdfs-tracker just have a try on autobuilded -_-# 3 [OK]
ecarpo/fastdfs 3
imlzw/fastdfs-tracker fastdfs的tracker服务 3 [OK]
。。。。。。。。。。。。
[root@bogon ~]#
拉取delron/fastdfs 版 ,当然你也可以选择其他的镜像,配置会有所不同,有些镜像内没有Nginx相关配置
docker pull delron/fastdfs
构建tracker容器,用于启动跟踪服务器,起到调度的作用。
docker run -d --network=host --name tracker -v /var/fdfs/tracker:/var/fdfs delron/fastdfs tracker
构建Storage容器 修改自己的ip 192.168.230.137
docker run -d --network=host --name storage -e TRACKER_SERVER=192.168.230.137:22122 -v /var/fdfs/storage:/var/fdfs -e GROUP_NAME=group1 delron/fastdfs storage
可以查看一下 docker ps
进入容器修改配置
docker exec -it tracker bash
修改配置
vi /etc/fdfs/client.conf
将配置 tracker_server=你自己的ip:22122
到这其实fastDFS就配好了
整合项目测试
整合SpringBoot和 swagger-ui接口文档 进行图片上传测试
pom.xml中导入依赖:
<!--Swagger-UI API文档生产工具-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!-- FastDFS依赖 -->
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
<version>1.26.5</version>
</dependency>
在yml文件中配置信息
spring:
servlet:
multipart:
# 最大支持文件大小
max-file-size: 100MB
# 最大支持请求大小
max-request-size: 100MB
# 分布式文件系统FDFS配置
fdfs:
# 链接超时
connect-timeout: 600
# 读取时间
so-timeout: 600
# 生成缩略图参数
thumb-image:
width: 150
height: 150
#连接FastDFS服务器
tracker-list: 192.168.230.137:22122
添加Swagger-UI的配置
package com.jq.swagger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi(){
//访问地址:http://localhost:8080/swagger-ui.html#/
//不一定是8080,启用那个服务就用那个端口
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//为当前包下controller生成API文档
.apis(RequestHandlerSelectors.basePackage("com.jq.order.controller"))
//为有@Api注解的Controller生成API文档
//.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
//为有@ApiOperation注解的方法生成API文档
// .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("订单模块api接口文档") // 文档标题
.description("订单模块api接口文档") // 文档描述
.contact("jq") // 设置联系人
.version("1.0") //版本号
.build();
}
}
添加FastDFS配置
package com.jq.fastdfs;
import com.github.tobato.fastdfs.FdfsClientConfig;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableMBeanExport;
import org.springframework.context.annotation.Import;
import org.springframework.jmx.support.RegistrationPolicy;
@Configuration
@Import(FdfsClientConfig.class)
// Jmx重复注册bean的问题
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
public class DfsConfig {
}
创建 FileDfsUtil 工具类
package com.jq.fastdfs;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
@Component
public class FileDfsUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(FileDfsUtil.class);
@Resource
private FastFileStorageClient storageClient ;
/**
* 上传文件*/
public String upload(MultipartFile file) throws Exception{
StorePath storePath = storageClient.uploadFile(file.getInputStream(),file.getSize(), FilenameUtils.getExtension(file.getOriginalFilename()),null);
return storePath.getFullPath() ;
}
/**
* 删除文件
*/
public void deleteFile(String fileUrl) {
if (StringUtils.isEmpty(fileUrl)) {
LOGGER.info("fileUrl == >>文件路径为空...");
return;
}
try {
StorePath storePath = StorePath.parseFromUrl(fileUrl);
storageClient.deleteFile(storePath.getGroup(), storePath.getPath());
} catch (Exception e) {
LOGGER.info(e.getMessage());
}
}
/**
* 下载文件
*
* @param imgName 文件URL
* @return 文件字节
* @throws IOException
*/
public byte[] downloadFile(String imgName) throws IOException {
String group = imgName.substring(0, imgName.indexOf("/"));
String path = imgName.substring(imgName.indexOf("/") + 1);
DownloadByteArray downloadByteArray = new DownloadByteArray();
byte[] bytes = fastFileStorageClient.downloadFile(group, path, downloadByteArray);
return bytes;
}
}
新建File控制层 注意在com.jq.order.controller目录下建不然访问不到
package com.jq.order.controller;
import com.jq.fastdfs.FileDfsUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
@RestController
@RequestMapping("FileController")
@Api(tags = "FileController", description = "上传控制器")
public class FileController {
@Resource
private FileDfsUtil fileDfsUtil ;
/**
* 文件上传
*/
@ApiOperation(value="上传文件", notes="测试FastDFS文件上传")
@RequestMapping(value = "/uploadFile",headers="content-type=multipart/form-data", method = RequestMethod.POST)
public ResponseEntity<String> uploadFile (@ApiParam("属性名") @RequestParam("file") MultipartFile file){
String result ;
try{
String path = fileDfsUtil.upload(file) ;
if (!StringUtils.isEmpty(path)){
result = path ;
} else {
result = "上传失败" ;
}
} catch (Exception e){
e.printStackTrace() ;
result = "服务异常" ;
}
return ResponseEntity.ok(result);
}
/**
* 文件删除
*/
@ApiOperation(value="删除文件", notes="测试FastDFS\"删除文件")
@RequestMapping(value = "/deleteByPath", method = RequestMethod.GET)
public ResponseEntity<String> deleteByPath (){
String filePathName = "group1/M00/00/00/wKjmiWCky6eALOBVAAb5PMwaVHw792.jpg" ;
fileDfsUtil.deleteFile(filePathName);
return ResponseEntity.ok("SUCCESS") ;
}
/**
* 文件下载
*/
@ApiOperation(value="下载文件", notes="测试FastDFS\"下载文件")
@RequestMapping(value = "/download", method = RequestMethod.GET)
public void downloadFile(String imgName, HttpServletResponse response) throws IOException {
byte[] bytes = fileDfsUtil.downloadFile(imgName);
// 这里只是为了整合fastdfs,所以写死了文件格式。需要在上传的时候保存文件名。下载的时候使用对应的格式
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("imgName.jpg", "UTF-8"));
response.setCharacterEncoding("UTF-8");
ServletOutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
outputStream.write(bytes);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
启动项目 5057 为自己服务端口
访问 http://localhost:5057/swagger-ui.html
一 :上传步骤
选择一个照片,发请求测试
访问地址 192.168.230.137 为虚拟机ip:8888/图片.jpg
http://192.168.230.137:8888/group1/M00/00/00/wKjmiWCky6eALOBVAAb5PMwaVHw792.jpg
效果:
二:下载步骤
复制上传回调的图片全称
图片储存地址