赞
踩
最近有很多小伙伴问壹哥,大规模的文件存储该怎么做? 这个感觉很难实现呢。其实这个需求,并没有大家想的那么难以实现。今天壹哥就带着各位,用10分钟的时间来搞定这个需求。不信?那你就跟着壹哥一步步来,10分钟绝对带你学会。
FastDFS是一个开源的轻量级分布式文件系统,它特别适合海量的文件存储,具有文件上传、下载、文件同步和删除等功能。还可以存储各种类型文件,例如文本文件、声音文件、视频文件、图片文件等,可以用来做相册网站,还可以在电商网站中保存商品图片,在贴吧网站保存文章中的插图等。
FastDFS特点:
支持高并发存储,对高并发文件存储有负载均衡的功能;
同时支持存储容量的水平扩展,可以无限扩展增大存储容量;
支持容灾功能,因为内部有冗余备份功能,防止文件丢失;
高可用,管理端和存储端都有心跳检测功能。
不要惊讶,它就是这么强大!!!
接下来壹哥就给大家解读FastDFS流程图中展示的执行流程和原理:
FastDFS分为客户端client(我们的项目),tracker管理端(管理存储端服务器的,不存储文件),storage存储端(存储文件) 共三部分。
首先管理端tracker不存储具体的文件,它用来管理storage存储端集群服务器。tracker可以是一主多从,备机每隔一段时间ping主机,主机返回pong命令,具有心跳检测机制。如果主机宕机,不返回pong命令,那么备机会替代主机工作。请求进入靠tracker管理端分配,具体将文件存储到哪一个storage存储端服务器,具有负载均衡功能。
storage存储端服务器,是两台为一组,一台主机和一台备机。存储时向主机从存储,然后主机将文件同步到备机保存一份,冗余存储防止文件丢失。主机和备机之间也有心跳检测机制,防止主机宕机,高可用。存储端服务器可以水平无限扩展,这样理论上fastDFS分布式文件系统可以存储的文件大小是无限大。
客户端如果想要存储,发送请求到tracker管理端,管理端会返回给客户端一个存储端服务器的ip和端口,然后客户端向具体的存储端服务器中存储,存储后返回一个存储后的路径,并且文件会被自动重命名,防止文件重名。
壹哥在这里使用Docker容器化来搭建FastDFS。
docker pull morunchang/fastdfs
创建FastDFS管理端tracker容器。
docker run -d --name tracker --net=host morunchang/fastdfs sh tracker.sh
创建FastDFS存储端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
-
- # 创建storage容器例子:
- docker run -d --name storage --net=host -e TRACKER_IP=192.168.200.128:22122 -e GROUP_NAME=group1 morunchang/fastdfs sh storage.sh
使用的网络模式是–net=host;
其中<your tracker server address> 位置替换为你机器的ip地址即可;
其中<group name> 是组名,即storage的组,例如: group1、group2、group3等;
如果想要增加新的storage服务器,再次运行该命令,注意更换新组名即可。
进入storage的容器内部,修改nginx.conf。
# 进入到storage容器内部 docker exec -it storage /bin/bash
进入到容器内部后。
- #1. 通过命令来查询Nginx的安装位置:
- root@iZ8vb6w2xyjemtqcxtmaj4Z:/# whereis nginx
- #显示如下:
- nginx: /etc/nginx
-
- #2. 查看当前Nginx的进程
- root@iZ8vb6w2xyjemtqcxtmaj4Z:/# ps aux | grep nginx
- #显示如下:
- root 16 0.0 0.0 32480 1480 ? Ss 13:18 0:00 nginx: master process /etc/nginx/sbin/nginx
- nobody 100 0.0 0.0 33036 2116 ? S 14:15 0:00 nginx: worker process
- root 118 0.0 0.0 11272 728 pts/1 S+ 14:54 0:00 grep --color=auto nginx
在storage存储端容器的nginx中添加以下内容:
- #3. 修改Nginx的配置文件
- vi /etc/nginx/conf/nginx.conf
-
- #4. 修改Nginx配置内容
- server {
- listen 80;
- server_name localhost;
-
- location ~ /M00 {
- # storage 实际存储图片的位置
- root /data/fast_data/data;
- ngx_fastdfs_module;
- }
- }
-
- #5. 进入到Nginx sbin目录从新加载Nginx配置文件
- cd /etc/nginx/sbin
-
- #6. 重新加载配置文件, 让nginx配置生效
- ./nginx -s reload
修改后:
storage存储的位置/data/fast_data/data。
docker update --restart=always tracker docker update --restart=always storage
创建文件管理微服务fastdfsDemo,该工程主要用于实现文件上传以及文件删除等功能。创建微服务时,项目为Maven项目,不要选择骨架。
- <!-- 继承Spring boot工程 -->
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.1.5.RELEASE</version>
- </parent>
-
- <properties>
- <!-- 项目源码及编译输出的编码 -->
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-
- <!-- 项目编译JDK版本 -->
- <maven.compiler.source>1.8</maven.compiler.source>
- <maven.compiler.target>1.8</maven.compiler.target>
-
- <!-- 依赖包版本管理 -->
- <spring.boot.version>2.1.5.RELEASE</spring.boot.version>
- <fastdfs.client.version>1.27.0.0</fastdfs.client.version>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- <version>${spring.boot.version}</version>
- </dependency>
- <dependency>
- <groupId>net.oschina.zcx7878</groupId>
- <artifactId>fastdfs-client-java</artifactId>
- <version>${fastdfs.client.version}</version>
- </dependency>
- </dependencies>
在resources文件夹下创建fasfDFS的配置文件fdfs_client.conf。
- connect_timeout = 60
- network_timeout = 60
- charset = UTF-8
- http.tracker_http_port = 80
- tracker_server = 192.168.200.128: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: fastdfs-demo
- server:
- port: 9001
max-file-size是单个文件大小,max-request-size是设置总上传的数据大小。
创建启动类
创建com.qianfeng包,创建启动类FastDFSApplication。
- package com.qianfeng;
-
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
-
- /**
- * 项目启动类
- * @Author 千锋壹哥
- */
- @SpringBootApplication
- public class FastDFSApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(FastDFSApplication.class, args);
- }
- }
文件上传一般都有文件的名字、文件的内容、文件的扩展名、文件的md5值、文件的作者等相关属性,我们可以创建一个对象封装这些属性。我们先创建com.qianfeng.pojo.FastDFSFile文件,代码如下:
- package com.qianfeng.pojo;
-
- /**
- * 自定义封装, 文件实体类
- * @Author 千锋壹哥
- */
- 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 ...
- }
创建FastDFSClient类,存放在com.qianfeng.util下,在该类中实现FastDFS信息获取以及文件的相关操作,代码如下:
- package com.qianfeng.util;
-
- import com.qianfeng.pojo.FastDFSFile;
- import org.csource.common.NameValuePair;
- import org.csource.fastdfs.*;
- import org.slf4j.LoggerFactory;
- import org.springframework.core.io.ClassPathResource;
-
- import java.io.ByteArrayInputStream;
- import java.io.IOException;
- import java.io.InputStream;
-
- /**
- * 上传下载等文件操作工具类
- * @Author 千锋壹哥
- */
- 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;
- }
- }
创建一个FileController,在该控制器中实现文件上传操作,代码如下:
- package com.qianfeng.controller;
-
- import com.qianfeng.pojo.FastDFSFile;
- import com.qianfeng.util.FastDFSClient;
- import org.springframework.web.bind.annotation.*;
- import org.springframework.web.multipart.MultipartFile;
-
- import java.io.IOException;
-
- /**
- * 文件操作controller接口
- * @Author 千锋壹哥
- */
- @RestController
- @CrossOrigin
- @RequestMapping("/file")
- public class FileController {
-
- /**
- * 上传接口
- * @param file 接收文件参数, 参数名必须叫做file
- * @Author 千锋壹哥
- */
- @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;
- }
-
- /**
- * 上传文件到FastDFS分布式文件系统
- * @param multipartFile
- * @Author 千锋壹哥
- */
- 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;
- }
- }
测试步骤:
选择post请求方式,输入请求地址 http://localhost:9001/file/upload
填写Headers
Key:Content-Type Value:multipart/form-data
填写body。选择form-data 然后选择文件file 点击添加文件,最后发送即可。
postman填写信息填写如下步骤所示。
注意Headers请求头中内容:
注意body请求体中内容:
上传结果后,就可以通过浏览器访问显示如下效果,这个小姐姐你想要吗?
至此,各位小伙伴是否都已经跟着壹哥学会了FastDFS的使用了呢?
现在挺多企业都是购买阿里云或者华为云等云服务器,然后在这些云服务器上再手动部署FastDFS,这样比直接购买使用阿里的OSS对象存储服务更节省经费,这也就是FastDFS的魅力所在。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。