一、体系结构
二、上传流程
什么是fastDFS:
fastDFS是一个底层使用C语言编写的, 开源的免费的分布式文件系统
fastDFS作用:
fastDFS主要作用就是上传文件, 下载文件, 删除文件等对文件的管理存储.
fastDFS运行原理:
1. fastDFS分为三部分: 客户端, 是指我们的购物项目。
tracker端, 也就是管理端是指管理服务器, 管理服务器不存储文件, 只负责管理存储端服务器。
storage端, 也就是存储端, 负责存储具体文件内容。
2. 存储端启动后会到管理端注册, 告诉管理端他的ip, 端口和状态, 我们需要存储文件的时候, 连接管理端,管理端会给我们返回具体服务器的ip地址, 端口号, 客户端就拿着ip和端口去调用存储端服务器存储文件,存储后会返回文件存储的路径和文件名, 存储端服务器会对文件自动重命名, 防止文件重名。
3. 管理端: 一台主机, 多台备机, 主备之间有心跳检测机制, 可以高可用,管理端有负载均衡的功能, 可以将请求均匀的分配给每一台存储端服务器处理。
存储端: 一台主机, 一台备机, 主备之间有心跳检测机制, 高可用, 存储的时候, 向主机中存储内容。
主机会将内容同步到备机, 主备之间存储的内容一样, 叫做冗余备份功能, 容灾效果强,存储端服务器可以理论上无限扩容, 扩展性强。
优点:
1. 管理端有高可用, 负载均衡功能, 可以承载高并发
2. 存储端冗余备份, 高可用, 无限扩容
缺点:
服务器集群需要企业自行搭建运维, 成本比较高.
使用场景:
适合大型互联网公司, 大规模存储任务使用.
fastDFS硬件服务器:
fastDFS服务器叫做存储服务器: 要求读写效率高
1. 如果经费充足: 可以购买ibm的nas服务器
2. 如果经费不够充足: 一般可以使用8块硬盘组成raid10磁盘阵列系统
硬盘分类: 5400转
7200转
12000转 读最快500多兆每秒, 写最快300多兆每秒
三、FastDFS搭建
我们使用Docker搭建FastDFS的开发环境
拉取镜像
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=<your tracker server address>:22122 -e GROUP_NAME=<group name> morunchang/fastdfs sh storage.sh
- 使用的网络模式是–net=host, <your tracker server address> 替换为你机器的Ip即可
- <group name> 是组名,即storage的组
- 如果想要增加新的storage服务器,再次运行该命令,注意更换 新组名
修改nginx的配置
进入storage的容器内部,修改nginx.conf
docker exec -it storage /bin/bash
进入后
vi /data/nginx/conf/nginx.conf
添加以下内容
location /group1/M00 {
proxy_next_upstream http_502 http_504 error timeout invalid_header;
proxy_cache http-cache;
proxy_cache_valid 200 304 12h;
proxy_cache_key $uri$is_args$args;
proxy_pass http://fdfs_group1;
expires 30d;
}
退出容器
exit
重启storage容器
docker restart storage
四、文件存储微服务
创建文件管理微服务changgou_service_file,该工程主要用于实现文件上传以及文件删除等功能。
(1)修改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>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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>
(2)在resources文件夹下创建fasfDFS的配置文件fdfs_client.conf
connect_timeout = 60
network_timeout = 60
charset = UTF-8
http.tracker_http_port = 8080
tracker_server = 192.168.200.128:22122
connect_timeout:连接超时时间,单位为秒。
network_timeout:通信超时时间,单位为秒。发送或接收数据时。假设在超时时间后还不能发送或接收数据,则本次网络通信失败
charset: 字符集
http.tracker_http_port :.tracker的http端口
tracker_server: tracker服务器IP和端口设置
(3)在resources文件夹下创建application.yml
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
server:
port: 9008
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:6868/eureka
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
max-file-size是单个文件大小,max-request-size是设置总上传的数据大小
(4)启动类 创建com.changgou包,创建启动类FileApplication
@SpringBootApplication
@EnableEurekaClient
public class FileApplication {
public static void main(String[] args) {
SpringApplication.run(FileApplication.class);
}
}
五、文件上传
1.文件信息封装
文件上传一般都有文件的名字、文件的内容、文件的扩展名、文件的md5值、文件的作者等相关属性,我们可以创建一个对象封装这些属性,代码如下:
创建com.changgou.file.pojo.FastDFSFile
public class FastDFSFile {
//文件名字
private String name;
//文件内容
private byte[] content;
//文件扩展名
private String ext;
//文件MD5摘要值
private String md5;
//文件创建作者
private String author;
public FastDFSFile(String name, byte[] content, String ext, String height,
String width, String author) {
super();
this.name = name;
this.content = content;
this.ext = ext;
this.author = author;
}
public FastDFSFile(String name, byte[] content, String ext) {
super();
this.name = name;
this.content = content;
this.ext = ext;
}
// getter and setter ...
}
2.文件操作
创建FastDFSClient类,放在com.itheima.file.util下在该类中实现FastDFS信息获取以及文件的相关操作,
代码如下:
public class FastDFSClient {
private static org.slf4j.Logger logger = LoggerFactory.getLogger(FastDFSClient.class);
/***
* 初始化加载FastDFS的TrackerServer配置
*/
static {
try {
String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();
ClientGlobal.init(filePath);
} catch (Exception e) {
logger.error("FastDFS Client Init Fail!",e);
}
}
/***
* 文件上传
* @param file
* @return
*/
public static String[] upload(FastDFSFile file) {
//获取文件的作者
NameValuePair[] meta_list = new NameValuePair[1];
meta_list[0] = new NameValuePair("author", file.getAuthor());
//接收返回数据
String[] uploadResults = null;
StorageClient storageClient=null;
try {
//创建StorageClient客户端对象
storageClient = getTrackerClient();
/***
* 文件上传
* 1)文件字节数组
* 2)文件扩展名
* 3)文件作者
*/
uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
} catch (Exception e) {
logger.error("Exception when uploadind the file:" + file.getName(), e);
}
if (uploadResults == null && storageClient!=null) {
logger.error("upload file fail, error code:" + storageClient.getErrorCode());
}
//获取组名
String groupName = uploadResults[0];
//获取文件存储路径
String remoteFileName = uploadResults[1];
return uploadResults;
}
/***
* 获取文件信息
* @param groupName:组名
* @param remoteFileName:文件存储完整名
* @return
*/
public static FileInfo getFile(String groupName, String remoteFileName) {
try {
StorageClient storageClient = getTrackerClient();
return storageClient.get_file_info(groupName, remoteFileName);
} catch (Exception e) {
logger.error("Exception: Get File from Fast DFS failed", e);
}
return null;
}
/***
* 文件下载
* @param groupName
* @param remoteFileName
* @return
*/
public static InputStream downFile(String groupName, String remoteFileName) {
try {
//创建StorageClient
StorageClient storageClient = getTrackerClient();
//下载文件
byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
InputStream ins = new ByteArrayInputStream(fileByte);
return ins;
} catch (Exception e) {
logger.error("Exception: Get File from Fast DFS failed", e);
}
return null;
}
/***
* 文件删除
* @param groupName
* @param remoteFileName
* @throws Exception
*/
public static void deleteFile(String groupName, String remoteFileName)
throws Exception {
//创建StorageClient
StorageClient storageClient = getTrackerClient();
//删除文件
int i = storageClient.delete_file(groupName, remoteFileName);
}
/***
* 获取Storage组
* @param groupName
* @return
* @throws IOException
*/
public static StorageServer[] getStoreStorages(String groupName)
throws IOException {
//创建TrackerClient
TrackerClient trackerClient = new TrackerClient();
//获取TrackerServer
TrackerServer trackerServer = trackerClient.getConnection();
//获取Storage组
return trackerClient.getStoreStorages(trackerServer, groupName);
}
/***
* 获取Storage信息,IP和端口
* @param groupName
* @param remoteFileName
* @return
* @throws IOException
*/
public static ServerInfo[] getFetchStorages(String groupName,
String remoteFileName) throws IOException {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName);
}
/***
* 获取Tracker服务地址
* @return
* @throws IOException
*/
public static String getTrackerUrl() throws IOException {
return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":"+ClientGlobal.getG_tracker_http_port()+"/";
}
/***
* 获取Storage客户端
* @return
* @throws IOException
*/
private static StorageClient getTrackerClient() throws IOException {
TrackerServer trackerServer = getTrackerServer();
StorageClient storageClient = new StorageClient(trackerServer, null);
return storageClient;
}
/***
* 获取Tracker
* @return
* @throws IOException
*/
private static TrackerServer getTrackerServer() throws IOException {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
return trackerServer;
}
}
3.文件上传
创建一个FileController,在该控制器中实现文件上传操作,代码如下:
@RestController
@CrossOrigin
public class FileController {
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file) {
String path ="";
try {
path=saveFile(file);
System.out.println(path);
} catch (Exception e) {
e.printStackTrace();
}
return path;
}
/**
* @param multipartFile
* @return
* @throws IOException
*/
public String saveFile(MultipartFile multipartFile) throws IOException {
//1. 获取文件名
String fileName = multipartFile.getOriginalFilename();
//2. 获取文件内容
byte[] content = multipartFile.getBytes();
//3. 获取文件扩展名
String ext = "";
if (fileName != null && !"".equals(fileName)) {
ext = fileName.substring(fileName.lastIndexOf("."));
}
//4. 创建文件实体类对象
FastDFSFile fastDFSFile = new FastDFSFile(fileName, content, ext);
//5. 上传
String[] uploadResults = FastDFSClient.upload(fastDFSFile);
//6. 拼接上传后的文件的完整路径和名字, uploadResults[0]为组名, uploadResults[1]为文件名称和路径
String path = FastDFSClient.getTrackerUrl() + uploadResults[0] + "/" + uploadResults[1];
//7. 返回
return path;
}
}
六、Postman测试文件上传
步骤:
1、选择post请求方式,输入请求地址 http://localhost:9007/upload
2、填写Headers
Key:Content-Type
Value:multipart/form-data
3、填写body
选择form-data 然后选择文件file 点击添加文件,最后发送即可。