1 分布式文件系统
- 分布式文件系统(Distributed File System):文件系统管理的物理存储资源不一定直接连接在本地节点上,而是通过计算机网络与节点相连。
- 传统文件管理系统管理的文件存储在本机。
- 分布式文件管理系统管理的文件存储在很多台机器上,这些机器通过网络连接,被同一管理。无论是上传或者访问文件,都需要通过管理中心来访问。
2 FastDFS
- FastDFS是由淘宝的余庆用C语言开发的一个轻量级、高性能的开源分布式文件系统。
- 适合有大容量存储需求的应用或系统。
- 功能:
- 文件存储。
- 文件同步。
- 文件访问(上传、下载)。
- 存取负载均衡。
- 在线扩容。
- 同类的分布式文件系统有谷歌的GFS、HDFS(Hadoop)、TFS(淘宝)。
- 网站:happyfish100 - Overview
3 FastDFS架构
1、架构图
- Tracker Server:跟踪服务器,主要负责调度Storage节点与Client通信。在访问上起负载均衡、记录Storage节点的运行状态等作用,是连接Client和Storage节点的枢纽。
- Storage Server:存储服务器,保存文件和文件的元数据(meta data)。每个storage server会启动一个单独的线程主动向Tracker cluster中每个Tracker server保存其状态信息,包括磁盘使用情况、文件同步情况以及文件上传下载次数统计等信息。
- Group:文件组,多台Storage server的集群。上传一个文件到同组内的一台机器上后,FastDFS会将该文件即时同步到同组内的其他所有机器上,起到备份的作用。不同组的服务器,保存的数据不同,而且项目独立,不进行通信。
- Tracker Cluster:跟踪服务器的集群,由一组跟踪服务器(Tracker Server)组成。
- Storage Cluster:存储集群,由多个Group组成。
2、上传、下载流程
- 上传:
- 上传步骤:
- Client通过Tracker查找可用的Storage。
- Tracker向Client返回一台可用的Storage的IP地址、端口号。
- Client直接通过该IP地址、端口号与其中一台Storage建立连接并进行文件上传。
- 上传完成,Storage返回一个文件ID。
- 下载:
下载步骤:
- Client通过Tracker查找要下载文件所在的Storage。
- Tracker向Client返回包含该文件的Storage的IP地址、端口号。
- Client直接通过该IP地址、端口号与Storage建立连接。
- 下载指定文件。
4 使用FastDFS客户端
1、依赖
<dependency> <groupId>com.github.tobato</groupId> <artifactId>fastdfs-client</artifactId> <version>${fastDFS.client.version}</version> </dependency>
2、配置
fdfs: so-timeout: 1501 # 超时时间 connect-timeout: 601 # 连接超时时间 thumb-image: # 缩略图 width: 60 height: 60 tracker-list: # tracker地址 - 192.168.56.101:22122
3、配置类
package com.leyou.upload.config; 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) @EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)// 解决jmx重复注册Bean的问题 public class FastClientImporter { }
4、Service
package com.leyou.upload.service; import com.github.tobato.fastdfs.domain.StorePath; import com.github.tobato.fastdfs.service.FastFileStorageClient; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.util.Arrays; import java.util.List; @Service public class UploadService { @Autowired private FastFileStorageClient storageClient; private static final List<String> CONTENT_TYPES = Arrays.asList("image/gif", "image/jpeg"); private static final Logger LOGGER = LoggerFactory.getLogger(UploadService.class); public String uploadImage(MultipartFile file) { String originalFilename = file.getOriginalFilename(); // 1、校验文件类型 String contentType = file.getContentType(); if (!CONTENT_TYPES.contains(contentType)) { LOGGER.info("文件类型不合法:{}", originalFilename); return null; } try { // 2、校验文件内容 BufferedImage bufferedImage = ImageIO.read(file.getInputStream()); if (bufferedImage == null) { LOGGER.info("文件内容不合法:{}", originalFilename); return null; } // 3、保存到服务器 //file.transferTo(new File("E:\\image\\" + originalFilename)); String ext = StringUtils.substringAfterLast(originalFilename, "."); StorePath storePath = this.storageClient.uploadFile(file.getInputStream(), file.getSize(), ext, null); // 4、返回url //return "http://image.leyou.com/" + originalFilename; return "http://image.leyou.com/" + storePath.getFullPath(); } catch (Exception e) { LOGGER.info("服务器内部错误:{}", originalFilename); e.printStackTrace(); } return null; } }