赞
踩
FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。
回顾:Dubbo工作流程

FastDFS工作流程

FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
FastDFS 架构包括 Tracker server 和 Storage server。客户端请求 Tracker server 进行文件上传、下载,通过Tracker server 调度最终由 Storage server 完成文件上传和下载。
Tracker server 作用是负载均衡和调度,通过 Tracker server 在文件上传时可以根据一些策略找到Storage server 提供文件上传服务。可以将 tracker 称为追踪服务器或调度服务器。Storage server 作用是文件存储,客户端上传的文件最终存储在 Storage 服务器上,Storageserver 没有实现自己的文件系统而是利用操作系统的文件系统来管理文件。可以将storage称为存储服务器。


客户端上传文件后存储服务器将文件 ID 返回给客户端,此文件 ID 用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。

组名:文件上传后所在的 storage 组名称,在文件上传成功后有storage 服务器返回,需要客户端自行保存。
虚拟磁盘路径:storage 配置的虚拟路径,与磁盘选项store_path*对应。如果配置了store_path0 则是 M00,如果配置了 store_path1 则是 M01,以此类推。
数据两级目录:storage 服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件。
文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储服务器 IP 地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息。
我们使用Docker搭建FastDFS的开发环境,虚拟机中已经下载了fastdfs的镜像,可以通过docker images查看,如下图:

拉取镜像
docker pull morunchang/fastdfs
运行tracker
docker run -d --name tracker --net=host morunchang/fastdfs sh tracker.sh
运行storage
docker run -d --name storage --net=host -e TRACKER_IP=192.168.211.132:22122 -e GROUP_NAME=group1 morunchang/fastdfs sh storage.sh
Nginx在这里主要提供对FastDFS图片访问的支持,Docker容器中已经集成了Nginx,我们需要修改nginx的配置,进入storage的容器内部,修改nginx.conf
docker exec -it storage /bin/bash
进入后
vi /etc/nginx/conf/nginx.conf
添加以下内容

上图配置如下:
location ~ /M00 {
root /data/fast_data/data;
ngx_fastdfs_module;
}
禁止缓存:
add_header Cache-Control no-store;
退出容器
exit
重启storage容器
docker restart storage
查看启动容器docker ps
9f2391f73d97 morunchang/fastdfs "sh storage.sh" 12 minutes ago Up 12 seconds storage
e22a3c7f95ea morunchang/fastdfs "sh tracker.sh" 13 minutes ago Up 13 minutes tracker
开启启动设置
docker update --restart=always tracker
docker update --restart=always storage
创建文件管理微服务changgou-service-file,该工程主要用于实现文件上传以及文件删除等功能。
修改pom.xml,引入依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>changgou-service</artifactId> <groupId>com.changgou</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>changgou-service-file</artifactId> <description>文件上传工程</description> <!--依赖包--> <dependencies> <!--FastDFS客户端程序包--> <dependency> <groupId>net.oschina.zcx7878</groupId> <artifactId>fastdfs-client-java</artifactId> <version>1.27.0.0</version> </dependency> <dependency> <groupId>com.changgou</groupId> <artifactId>changgou-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>
在resources文件夹下创建fasfDFS的配置文件fdfs_client.conf
#连接超时时间
connect_timeout=60
#网络请求超时时间
network_timeout=60
#字符集编码
charset=UTF-8
#Tracker的Http请求端口
http.tracker_http_port=8080
#Tracker的TCP通信端口
tracker_server=192.168.211.132:22122
connect_timeout:连接超时时间,单位为秒。
network_timeout:通信超时时间,单位为秒。发送或接收数据时。假设在超时时间后还不能发送或接收数据,则本次网络通信失败
charset: 字符集
http.tracker_http_port :.tracker的http端口
tracker_server: tracker服务器IP和端口设置
在resources文件夹下创建application.yml
spring: servlet: multipart: #文件上传最大大小 max-file-size: 10MB #文件请求最大大小 max-request-size: 10MB application: name: file server: port: 18082 eureka: client: service-url: defaultZone: http://127.0.0.1:7001/eureka instance: prefer-ip-address: true feign: hystrix: enabled: true
max-file-size是单个文件大小,max-request-size是设置总上传的数据大小
创建com.changgou包,创建启动类FileApplication
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})//排除掉数据库自动加载
@EnableEurekaClient
public class FileApplication {
public static void main(String[] args) {
SpringApplication.run(FileApplication.class,args);
}
}
这里禁止了DataSource的加载创建,否则会报以下错误。

文件上传一般都有文件的名字、文件的内容、文件的扩展名、文件的md5值、文件的作者等相关属性,我们可以创建一个对象封装这些属性,代码如下:
创建com.changgou.file.FastDFSFile代码如下:
import java.io.Serializable; /** * 封装文件上传信息 * 时间: * Author: * type: * size: * 附加信息: * 后缀: * 文件内容->文件的字节数组 */ public class FastDFSFile implements Serializable { //文件名字 private String name; //文件内容 private byte[] content; //文件扩展名 jpg png gif private String ext; //文件MD5摘要值 private String md5; //文件创建作者 private String author; public FastDFSFile(String name, byte[] content, String ext, String md5, String author) { this.name = name; this.content = content; this.ext = ext; this.md5 = md5; this.author = author; } public FastDFSFile(String name, byte[] content, String ext) { this.name = name; this.content = content; this.ext = ext; } public FastDFSFile() { } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { this.name = name; } /** * 获取 * @return content */ public byte[] getContent() { return content; } /** * 设置 * @param content */ public void setContent(byte[] content) { this.content = content; } /** * 获取 * @return ext */ public String getExt() { return ext; } /** * 设置 * @param ext */ public void setExt(String ext) { this.ext = ext; } /** * 获取 * @return md5 */ public String getMd5() { return md5; } /** * 设置 * @param md5 */ public void setMd5(String md5) { this.md5 = md5; } /** * 获取 * @return author */ public String getAuthor() { return author; } /** * 设置 * @param author */ public void setAuthor(String author) { this.author = author; } @Override public String toString() { return "FastDFSFile{name = " + name + ", content = " + content + ", ext = " + ext + ", md5 = " + md5 + ", author = " + author + "}"; } }
(可选)测试文件相关操作:
import org.csource.fastdfs.*; import org.junit.Test; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.net.InetSocketAddress; public class FastdfsClientTest { /** * 文件上传 * @throws Exception */ @Test public void upload() throws Exception { //加载全局的配置文件 ClientGlobal.init("E:\\Java\\IDEA\\changgou\\changgou-parent\\changgou-service\\changgou-service-file\\src\\main\\resources\\fdfs_client.conf"); //创建TrackerClient客户端对象 TrackerClient trackerClient = new TrackerClient(); //通过TrackerClient对象获取TrackerServer信息 TrackerServer trackerServer = trackerClient.getConnection(); //获取StorageClient对象 StorageClient storageClient = new StorageClient(trackerServer, null); //执行文件上传 String[] jpgs = storageClient.upload_file("C:\\Users\\44638\\Desktop\\1.jpg", "jpg", null); for (String jpg : jpgs) { System.out.println(jpg); } } @Test public void delete() throws Exception { //加载全局的配置文件 ClientGlobal.init("E:\\Java\\IDEA\\changgou\\changgou-parent\\changgou-service\\changgou-service-file\\src\\main\\resources\\fdfs_client.conf"); //创建TrackerClient客户端对象 TrackerClient trackerClient = new TrackerClient(); //通过TrackerClient对象获取TrackerServer信息 TrackerServer trackerServer = trackerClient.getConnection(); //获取StorageClient对象 StorageClient storageClient = new StorageClient(trackerServer, null); //执行文件上传 int group1 = storageClient.delete_file("group1", "M00/00/00/wKjThF9160GAO4T-AABLH3E1MLc898.jpg"); System.out.println(group1); } @Test public void download() throws Exception { //加载全局的配置文件 ClientGlobal.init("E:\\Java\\IDEA\\changgou\\changgou-parent\\changgou-service\\changgou-service-file\\src\\main\\resources\\fdfs_client.conf"); //创建TrackerClient客户端对象 TrackerClient trackerClient = new TrackerClient(); //通过TrackerClient对象获取TrackerServer信息 TrackerServer trackerServer = trackerClient.getConnection(); //获取StorageClient对象 StorageClient storageClient = new StorageClient(trackerServer, null); /** * 执行文件上传 * 参数1:指定要下载的组名 * 参数2:指定要下载的远程文件路径 */ byte[] bytes = storageClient.download_file("group1", "M00/00/00/wKjThF9162WAWgfRAABLH3E1MLc672.jpg"); File file = new File("C:\\Users\\44638\\Desktop\\2.jpg"); FileOutputStream fileOutputStream = new FileOutputStream(file); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); bufferedOutputStream.write(bytes); bufferedOutputStream.close(); fileOutputStream.close(); } //获取文件的信息数据 @Test public void getFileInfo() throws Exception { //加载全局的配置文件 ClientGlobal.init("E:\\Java\\IDEA\\changgou\\changgou-parent\\changgou-service\\changgou-service-file\\src\\main\\resources\\fdfs_client.conf"); //创建TrackerClient客户端对象 TrackerClient trackerClient = new TrackerClient(); //通过TrackerClient对象获取TrackerServer信息 TrackerServer trackerServer = trackerClient.getConnection(); //获取StorageClient对象 StorageClient storageClient = new StorageClient(trackerServer, null); //执行文件上传 FileInfo group1 = storageClient.get_file_info("group1", "M00/00/00/wKjThF9162WAWgfRAABLH3E1MLc672.jpg"); System.out.println(group1); } //获取组相关的信息 @Test public void getGroupInfo() throws Exception { //加载全局的配置文件 ClientGlobal.init("E:\\Java\\IDEA\\changgou\\changgou-parent\\changgou-service\\changgou-service-file\\src\\main\\resources\\fdfs_client.conf"); //创建TrackerClient客户端对象 TrackerClient trackerClient = new TrackerClient(); //通过TrackerClient对象获取TrackerServer信息 TrackerServer trackerServer = trackerClient.getConnection(); StorageServer group1 = trackerClient.getStoreStorage(trackerServer, "group1"); System.out.println(group1.getStorePathIndex()); //组对应的服务器的地址 因为有可能有多个服务器. ServerInfo[] group1s = trackerClient.getFetchStorages(trackerServer, "group1", "M00/00/00/wKjThF9162WAWgfRAABLH3E1MLc672.jpg"); for (ServerInfo serverInfo : group1s) { System.out.println(serverInfo.getIpAddr()); System.out.println(serverInfo.getPort()); } } @Test public void getTrackerInfo() throws Exception { //加载全局的配置文件 ClientGlobal.init("E:\\Java\\IDEA\\changgou\\changgou-parent\\changgou-service\\changgou-service-file\\src\\main\\resources\\fdfs_client.conf"); //创建TrackerClient客户端对象 TrackerClient trackerClient = new TrackerClient(); //通过TrackerClient对象获取TrackerServer信息 TrackerServer trackerServer = trackerClient.getConnection(); InetSocketAddress inetSocketAddress = trackerServer.getInetSocketAddress(); System.out.println(inetSocketAddress); } }
创建com.changgou.util.FastDFSClient类,在该类中实现FastDFS信息获取以及文件的相关操作,代码如下:
import com.changgou.file.FastDFSFile; import org.csource.common.NameValuePair; import org.csource.fastdfs.*; import org.springframework.core.io.ClassPathResource; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; /** * 实现FastDFS文件管理 * 文件上传 * 文件下载 * 文件删除 * 文件信息获取 * Storage信息获取 * Tracker信息获取 */ 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 fastDFSFile 上传的文件信息封装->FastDFSFile * @return * 1:文件上传所存储的组名 * 2:文件存储路径 */ public static String[] upload(FastDFSFile fastDFSFile){ //获取文件作者 NameValuePair[] meta_list = new NameValuePair[1]; meta_list[0] =new NameValuePair("author",fastDFSFile.getAuthor()); /** * 文件上传后的返回值 */ String[] uploadResults = null; try { //创建一个TrackerClient访问客户端对象TrackerClient TrackerClient trackerClient = new TrackerClient(); //通过TrackerClient访问TrackerServer服务,获取连接信息 TrackerServer trackerServer = trackerClient.getConnection(); //通过TrackerServer的链接信息可以获取Storage的链接信息,创建StorageClient对象存储Storage的链接信息 StorageClient storageClient = new StorageClient(trackerServer, null); /** * 通过StorageClient 访问Storagem,实现文件上传,并且获取文件上传后的存储信息 * 1:上传文件的字节数组 * 2:文件的拓展名 jpg * 3:附加参数 比如:拍摄地址:深圳 * * upload【】 * upload[0]:文件上传所存储的组名,例如:group1 * upload[1]:文件存储路径,例如:M00/00/00/wKjThF9160GAO4T-AABLH3E1MLc898.jpg */ uploadResults = storageClient.upload_file(fastDFSFile.getContent(), fastDFSFile.getExt(), meta_list); } catch (Exception e) { e.printStackTrace(); } return uploadResults; } /** * 获取文件信息 * @param groupName:组名 * @param remoteFileName:文件存储完整名 * @return */ public static FileInfo getFile(String groupName, String remoteFileName){ try { //创建TrackerClient对象 TrackerClient trackerClient = new TrackerClient(); //通过TrackerClient获得TrackerServer信息 TrackerServer trackerServer =trackerClient.getConnection(); //通过TrackerServer获取StorageClient对象 StorageClient storageClient = new StorageClient(trackerServer,null); //获取文件信息 return storageClient.get_file_info(groupName,remoteFileName); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 文件下载 * @param groupName:组名 * @param remoteFileName:文件存储完整名 * @return */ public static InputStream downFile(String groupName, String remoteFileName){ try { //创建TrackerClient对象 TrackerClient trackerClient = new TrackerClient(); //通过TrackerClient对象创建TrackerServer TrackerServer trackerServer = trackerClient.getConnection(); //通过TrackerServer创建StorageClient StorageClient storageClient = new StorageClient(trackerServer,null); //通过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 { //创建TrackerClient对象 TrackerClient trackerClient = new TrackerClient(); //通过TrackerClient获取TrackerServer对象 TrackerServer trackerServer = trackerClient.getConnection(); //通过TrackerServer创建StorageClient StorageClient storageClient = new StorageClient(trackerServer,null); //通过StorageClient删除文件 storageClient.delete_file(groupName,remoteFileName); } catch (Exception e) { e.printStackTrace(); } } /** * 获取组信息 * @param groupName:组名 * @return */ 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:文件存储完整名 * @return */ 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服务地址 * @return */ 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 * @return * @throws Exception */ public static TrackerServer getTrackerServer() throws Exception{ //创建TrackerClient对象 TrackerClient trackerClient = new TrackerClient(); //通过TrackerClient获取TrackerServer对象 TrackerServer trackerServer = trackerClient.getConnection(); return trackerServer; } /** * 获取StorageClient * @return * @throws Exception */ public static StorageClient getStorageClient() throws Exception{ //获取TrackerServer TrackerServer trackerServer = getTrackerServer(); //通过TrackerServer创建StorageClient StorageClient storageClient = new StorageClient(trackerServer,null); return storageClient; } }
创建一个FileController,在该控制器中实现文件上传操作,代码如下:
import com.changgou.file.FastDFSFile; import com.changgou.util.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.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController @CrossOrigin public class FileController { /** * 文件上传 * * 接收页面的上传图片的请求 * 请求是一个POST * 请求路径 /upload * 返回值:图片的url * * @param file @RequestParam(name="file")可以不用写,如果前端写的就是file参数 * @return * @throws Exception */ @PostMapping(value = "/upload") public String upload(@RequestParam(value = "file") MultipartFile file) throws Exception { //封装一个FastDFSFile FastDFSFile fastDFSFile = new FastDFSFile( file.getOriginalFilename(), //文件名字 file.getBytes(), //文件字节数组 StringUtils.getFilenameExtension(file.getOriginalFilename()));//文件扩展名 //调用FastDFSClient工具类将文件传入到FastDFS中 String[] uploads = FastDFSClient.upload(fastDFSFile); //组装文件上传地址 return FastDFSClient.getTrackerUrl()+"/"+uploads[0]+"/"+uploads[1]; } }
注意,这里每次访问的端口是8080端口,访问的端口其实是storage容器的nginx端口,如果想修改该端口可以直接进入到storage容器,然后修改即可。
docker exec -it storage /bin/bash
vi /etc/nginx/conf/nginx.conf

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。