简介:java系列技术分享(持续更新中…)
初衷:一起学习、一起进步、坚持不懈
如果文章内容有误与您的想法不一致,欢迎大家在评论区指正
希望这篇文章对你有所帮助,欢迎点赞 收藏 ⭐留言更多文章请点击
项目开源地址
:https://github.com/happyfish100/fastdfs
官方论坛
: http://bbs.chinaunix.net/forum-240-1.html
FastDFS
是基于互联网应用的开源分布式文件系统,主要用于大中型网站存储资源文件,如图片、文档、音频、视频等。FastDFS 架构包括 Tracker Server
和 Storage Server
Storage Server : 存储服务器
,负责文件存储,文件同步/备份,提供文件访问接口,文件元数据管理。以 group 为单位,每个 group 内可以有多台 Storage Server,数据互为备份,达到容灾的目的。每个 Storage 在启动以后会主动连接 Tracker,告知自己所属 group 等存储相关信息,并保持周期性心跳。
Tracker Server: 跟踪调度服务器
,调度存储服务,负责文件访问的调度和负载均衡,负责管理所有的 Storage Server 和 group 组/卷。
Group: 组, 也可称为 Volume 卷。同组内服务器上的文件是完全相同的,同一组内的 Storage Server 之间是对等的,文件上传、删除等操作可以在任意一台 Storage Server 上进行。
扩容分组可以解决单一Storage Server 容量限制问题
组内构建存储服务器,解决单一节点故障问题
为了支持大容量存储,Storage 存储服务器采用了分组(或分卷)的方式。存储系统由一个或多个组组成,组与组之间的文件是相互独立的,所有组的文件容量累加就是整个存储系统中的文件容量。一个组可以由一台或多台存储服务器组成,一个组下的存储服务器中的文件都是相同的,组中的多台存储服务器起到了冗余备份和负载均衡的作用。
当组中增加了新的服务器时,系统会自动同步已有的文件,同步完成后,系统自动将新增的服务器切换至线上提供服务。
当存储空间不足时,可以动态添加组,只需要增加一台或多台服务器,并将它们配置为一个新的组,即可扩大存储系统的容量。当你的某个应用或者模块(对应的 group)的并发过高的时候,可以直接在 group 中增加若干个 Storage 来实现负载均衡。
生成文件名 : 文件名由 group名称/存储目录/两级子目录/file_id.后缀名 拼接而成。
例如: group/M00/00/00/wkiIoGjXKDSAE030aaCXPfGRATQ725.jpg
解读:
group1
:组名/卷名
。文件上传成功以后所在的 Storage 组名称,由 Storage 服务器返回。M00
:虚拟磁盘路径
。/00/00
:数据两级目录
Storage 服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件。wkiIoGjXKDSAE030aaCXPfGRATQ725
:file_id
,由 Storage Server IP、文件创建时间、文件大小、文件 crc32 和一个随机数组成,然后将这个二进制串进行 base64 编码,转换为字符串。<dependency>
<groupId>com.github.tobatogroupId>
<artifactId>fastdfs-clientartifactId>
<version>1.26.7version>
<exclusions>
<exclusion>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
exclusion>
exclusions>
dependency>
fdfs:
so-timeout: 1500
connect-timeout: 600
#缩略图生成参数
thumb-image:
width: 150
height: 150
#TrackerList参数,支持多个
tracker-list: 192.168.136.150:22122
## 可选,主要用于路径拼接
web-server-url: http://192.168.136.150:8888/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TanhuaServerApplication.class)
public class TestFastDFS {
//从调度服务器获取一个目标存储服务器,上传
@Autowired
private FastFileStorageClient client;
@Autowired
private FdfsWebServer webServer;// 获取存储服务器的请求URL 对应配置文件中的 web-server-url
@Test
public void testFileUpdate() throws FileNotFoundException {
//1、指定文件
File file = new File("D:\\test.jpg");
//2、文件上传
StorePath path = client.uploadFile(new FileInputStream(file),
file.length(), "jpg", null);
//3、拼接访问路径
String url = webServer.getWebServerUrl() + path.getFullPath();
}
}
public interface FdfsService {
String uploadFile(MultipartFile file);
String downloadFile(String fileUrl);
Boolean deleteFile(String fileUrl);
byte[] downloadMultipartFile(String fileUrl);
}
@Service
@Slf4j
public class FdfsServiceImpl implements FdfsService {
@Autowired
private FastDFSClientWrapper fastDFSClientWrapper;
/**
* 文件上传
* 最后返回fastDFS中的文件名称;group/M00/00/00/wkiIoGjXKDSAE030aaCXPfGRATQ725.jpg
*/
@Override
public String uploadFile(MultipartFile file) {
byte[] bytes = new byte[0];
try {
bytes = file.getBytes();
} catch (IOException e) {
log.error("获取文件错误");
e.printStackTrace();
}
//获取源文件名称
String originalFileName = file.getOriginalFilename();
//获取文件后缀
String extension = originalFileName.substring(originalFileName.lastIndexOf(".") + 1);
String fileName = file.getName();
//获取文件大小
long fileSize = file.getSize();
return fastDFSClientWrapper.uploadFile(bytes, fileSize, extension);
}
/**
* 文件下载
*
* @param fileUrl 当前对象文件名称
* @param response HttpServletResponse 内置对象
* @throws IOException
*/
public void downloadFile(String fileUrl, HttpServletResponse response) throws IOException {
byte[] bytes = fastDFSClientWrapper.downloadFile(fileUrl);
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("test.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();
}
}
}
@Override
public Boolean deleteFile(String fileUrl) {
try {
fastDFSClientWrapper.deleteFile(fileUrl);
} catch (IOException e) {
log.error("文件删除失败",e);
return false;
}
return true;
}
@Override
public byte[] downloadMultipartFile(String fileUrl) {
byte[] bytes = new byte[0];
MultipartFile file = null;
try {
bytes = fastDFSClientWrapper.downloadFile(fileUrl);
} catch (IOException e) {
log.error("文件下载失败",e);
}
return bytes;
}
/**
* 文件下载
* @param fileUrl 当前对象文件名称
* @throws IOException
*/
@Override
public String downloadFile(String fileUrl) {
byte[] bytes = new byte[0];
try {
bytes = fastDFSClientWrapper.downloadFile(fileUrl);
} catch (IOException e) {
log.error("文件下载失败",e);
}
return Base64.getEncoder().encodeToString(bytes);
}
}
@Component
@Slf4j
public class FastDFSClientWrapper {
@Autowired
private FastFileStorageClient fastFileStorageClient;
/**
* 文件上传
* 最后返回fastDFS中的文件名称;group/M00/00/00/wkiIoGjXKDSAE030aaCXPfGRATQ725.jpg
*
* @param bytes 文件字节
* @param fileSize 文件大小
* @param extension 文件扩展名
* @return fastDfs路径
*/
public String uploadFile(byte[] bytes, long fileSize, String extension) {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
StorePath storePath = fastFileStorageClient.uploadFile(byteArrayInputStream, fileSize, extension, null);
System.out.println(storePath.getGroup() + "==" + storePath.getPath() + "======" + storePath.getFullPath());
return storePath.getFullPath();
}
/**
* 下载文件
* 返回文件字节流大小
* @param fileUrl 文件URL
* @return 文件字节
*/
public byte[] downloadFile(String fileUrl) throws IOException {
String group = fileUrl.substring(0, fileUrl.indexOf("/"));
String path = fileUrl.substring(fileUrl.indexOf("/") + 1);
DownloadByteArray downloadByteArray = new DownloadByteArray();
byte[] bytes = fastFileStorageClient.downloadFile(group, path, downloadByteArray);
return bytes;
}
public boolean deleteFile(String fileUrl) throws IOException {
String group = fileUrl.substring(0, fileUrl.indexOf("/"));
String path = fileUrl.substring(fileUrl.indexOf("/") + 1);
fastFileStorageClient.deleteFile(group,path);
return true;
}
}