一.FastDFS介绍
FastDFS 是一个开源的高性能分布式文件系统(DFS)。 它的主要功能包括:文件存储,文件同步和文件访问,以及高容量和负载平衡。主要解决了海量数据存储问题,特别适合以中小文件(建议范围:4KB < file_size <500MB)为载体的在线服务。
FastDFS 系统有三个角色:跟踪服务器(Tracker Server)、存储服务器(Storage Server)和客户端(Client)。
Tracker Server:跟踪服务器,主要做调度工作,起到均衡的作用;负责管理所有的 storage server和 group,每个 storage 在启动后会连接 Tracker,告知自己所属 group 等信息,并保持周期性心跳。
Storage Server:存储服务器,主要提供容量和备份服务;以 group 为单位,每个 group 内可以有多台 storage server,数据互为备份。
Client:客户端,上传下载数据的服务器,也就是我们自己的项目所部署在的服务器。
存储节点Storage采用了分卷[Volume](或分组[group])的组织方式,存储系统由一个或多个组组成,组与组之间的文件是相互独立的,所有组的文件容量累加就是整个存储系统中的文件容量。
一个卷[Volume](组[group])可以由一台或多台存储服务器组成,一个组中的存储服务器中的文件都是相同的,组中的多台存储服务器起到了冗余备份和负载均衡的作用,数据互为备份,存储空间以group内容量最小的storage为准,所以建议group内的多个storage尽量配置相同,以免造成存储空间的浪费。
二.FastDFS服务器搭建
具体安装部署参考,注意,需要复制替换的文件,如果存在就不用替换
二.springboot整合FastDFS
刚开始按照帖子 ,用的1.26.1-RELEASE的fastdfs-client,在启动时发现报错ConnectManage的一个bean无法注册,因为已经注册过了,按照提示在配置文件中添加spring.main.allow-bean-definition-overriding=true ,再次启动发现还是存在错误,经过仔细查看这两个bean所在的类不一样,所以不能覆盖,而且都是在第三方jar包中,无法通过修改bean名称来解决,所以开始尝试用启动的时候重命名自己引入的这个类的bean的方式注册bean,后来发现之前存在项目中的bean是优先启动的,自己加入的这个jar包那个类的bean是后创建的,而且要在创建后才能去重命名, 如果重命名项目中以前的bean,则项目中别人使用该bean的地方都需要修改,影响比较大,只能尝试解决自己加入的这个包下的bean,后来在github官网( https://github.com/tobato/FastDFS_Client/blob/d866cd05e2930751c475cce8de6d109d532a0d93/src/test/java/com/github/tobato/fastdfs)看到,该jar包的新版本, 发现新版本中已经解决了该冲突, 所以使用该新版本,后来结合该github上的代码中的test内容,完成了开发;
下面是具体在开发中的实现:
引入jar包:
<!-- fdfs java客户端依赖 -->
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
<version>1.27.2</version>
</dependency>
启动类加注解:
@Import(FdfsClientConfig.class)
新增配置:
fdfs.connect-timeout=600
fdfs.tracker-list=xx.xx.xx.xx:22122
代码实现:
package com.xxx.xxx.filebusiness;
import java.io.IOException;
import java.net.URLEncoder;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.eos.runtime.core.TraceLoggerFactory;
import com.eos.system.logging.Logger;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.domain.proto.storage.DownloadByteArray;
import com.github.tobato.fastdfs.service.AppendFileStorageClient;
@Service("FastDFSClientService")
public class FastDFSClientService {
private Logger logger = TraceLoggerFactory.getLogger(FastDFSClientService.class);
@Autowired
private AppendFileStorageClient storageClient;
/**
* 上传文件
* @param file
* @return
* @throws IOException
*/
public String uploadFile(MultipartFile file) throws IOException {
byte[] bytes = file.getBytes();
String originalFileName = file.getOriginalFilename();
String extension = originalFileName.substring(originalFileName.lastIndexOf(".") + 1);
String fileName = file.getName();
long fileSize = file.getSize();
logger.info(originalFileName + ":" + fileName + ":" + fileSize + ":" + extension + ":" + bytes.length);
StorePath path = storageClient.uploadFile(null, file.getInputStream(), fileSize, extension);
return path.getFullPath();
}
/**
* 下载文件
* @param filePath
* @throws IOException
*/
public void downloadFile(String filePath, HttpServletResponse response) throws IOException{
String group = filePath.substring(0, filePath.indexOf("/"));
String path = filePath.substring(filePath.indexOf("/") + 1);
DownloadByteArray downloadByteArray = new DownloadByteArray();
byte[] bytes = storageClient.downloadFile(group, path, downloadByteArray);
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(filePath, "UTF-8"));
response.setCharacterEncoding("UTF-8");
ServletOutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
outputStream.write(bytes);
} catch (IOException e) {
logger.error("文件下载失败!", e);
} finally {
try {
outputStream.flush();
outputStream.close();
} catch (IOException e) {
logger.error("文件下载失败!", e);
}
}
}
}