0


搭建自己的云存储空间|FastDFS分布式文件系统考虑一下?

一. 前言

最近有很多小伙伴问壹哥,大规模的文件存储该怎么做? 这个感觉很难实现呢。其实这个需求,并没有大家想的那么难以实现。今天壹哥就带着各位,用10分钟的时间来搞定这个需求。不信?那你就跟着壹哥一步步来,10分钟绝对带你学会。

二. 浅析FastDFS分布式文件系统

FastDFS是一个开源的轻量级分布式文件系统,它特别适合海量的文件存储,具有文件上传、下载、文件同步和删除等功能。还可以存储各种类型文件,例如文本文件、声音文件、视频文件、图片文件等,可以用来做相册网站,还可以在电商网站中保存商品图片,在贴吧网站保存文章中的插图等。

FastDFS****特点:

  • 支持高并发存储,对高并发文件存储有负载均衡的功能;

  • 同时支持存储容量的水平扩展,可以无限扩展增大存储容量;

  • 支持容灾功能,因为内部有冗余备份功能,防止文件丢失;

  • 高可用,管理端和存储端都有心跳检测功能。

不要惊讶它就是这么强大****!!!

三. FastDFS执行流程和原理

接下来壹哥就给大家解读FastDFS流程图中展示的执行流程和原理****:

  • FastDFS分为客户端client(我们的项目),tracker管理端(管理存储端服务器的,不存储文件),storage存储端(存储文件) 共三部分。

  • 首先管理端tracker不存储具体的文件,它用来管理storage存储端集群服务器。tracker可以是一主多从,备机每隔一段时间ping主机,主机返回pong命令,具有心跳检测机制。如果主机宕机,不返回pong命令,那么备机会替代主机工作。请求进入靠tracker管理端分配,具体将文件存储到哪一个storage存储端服务器,具有负载均衡功能。

  • storage存储端服务器,是两台为一组,一台主机和一台备机。存储时向主机从存储,然后主机将文件同步到备机保存一份,冗余存储防止文件丢失。主机和备机之间也有心跳检测机制,防止主机宕机,高可用。存储端服务器可以水平无限扩展,这样理论上fastDFS分布式文件系统可以存储的文件大小是无限大。

  • 客户端如果想要存储,发送请求到tracker管理端,管理端会返回给客户端一个存储端服务器的ip和端口,然后客户端向具体的存储端服务器中存储,存储后返回一个存储后的路径,并且文件会被自动重命名,防止文件重名。

四. FastDFS安装部署

壹哥在这里使用Docker容器化来搭建FastDFS。

4.1****拉取镜像

docker pull morunchang/fastdfs

4.2****运行tracker

创建FastDFS管理端tracker容器。

docker run -d --name tracker --net=host morunchang/fastdfs sh tracker.sh

4.3****运行storage

创建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服务器,再次运行该命令,注意更换新组名即可。

*4.4***修改nginx的配置 **

进入storage的容器内部,修改nginx.conf。

# 进入到storage容器内部 docker exec -it storage /bin/bash

进入到容器内部后。

#1. 通过命令来查询Nginx的安装位置:
[email protected]:/# whereis nginx
#显示如下:
nginx: /etc/nginx

#2. 查看当前Nginx的进程
[email protected]:/# 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。

4.5设置开机启动容器

docker update --restart=always tracker docker update --restart=always storage

五. 代码实现

5.1****创建文件管理微服务

创建文件管理微服务fastdfsDemo,该工程主要用于实现文件上传以及文件删除等功能。创建微服务时,项目为Maven项目,不要选择骨架。

5.2****修改pom.xml引入依赖

<!-- 继承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>

5.3****创建fasfDFS的配置文件

在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和端口设置。

5.4****创建微服务配置文件

在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);
    }
}

**5.5 文件上传 **

5.5.1 文件信息封装

文件上传一般都有文件的名字、文件的内容、文件的扩展名、文件的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 ...
}

5.5.2 文件操作

创建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;
    }
}

5.5.3 文件上传

创建一个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;
    }
}

5.4 Postman测试文件上传

测试步骤:

  1. 选择post请求方式,输入请求地址 http://localhost:9001/file/upload

  2. 填写Headers

Key:Content-Type Value:multipart/form-data
  1. 填写body。选择form-data 然后选择文件file 点击添加文件,最后发送即可。

postman填写信息填写如下步骤所示。

注意Headers请求头中内容:

注意body请求体中内容:

上传结果后,就可以通过浏览器访问显示如下效果,这个小姐姐你想要吗?

六. 结语

至此,各位小伙伴是否都已经跟着壹哥学会了FastDFS的使用了呢?

现在挺多企业都是购买阿里云或者华为云等云服务器,然后在这些云服务器上再手动部署FastDFS,这样比直接购买使用阿里的OSS对象存储服务更节省经费,这也就是FastDFS的魅力所在。

标签: 服务器 网络 运维

本文转载自: https://blog.csdn.net/syc000666/article/details/126865348
版权归原作者 一一哥Sun 所有, 如有侵权,请联系我们删除。

“搭建自己的云存储空间|FastDFS分布式文件系统考虑一下?”的评论:

还没有评论