• FastDFS文件系统单机环境搭建并使用


    FastDFS文件系统单机环境搭建


    项目中使用,又把原来忘记的东西重新捡起来,整理一份

    FastDFS基本介绍

    FastDFS 系统三个角色

    Tracker Server


    Tracker Server: 跟踪服务器,主要做调度工作,起到均衡的作用;负责管理所有的 storage server
    和 group,每个 storage 在启动后会连接 Tracker,告知自己所属 group 等信息,并保持周期性心跳。

    Storage Server


    Storage Server:存储服务器,主要提供容量和备份服务;以 group 为单位,每个 group 内可以有多台 storage server,数据互为备份。

    Client

    Client:客户端,上传下载数据的服务器,也就是我们自己的项目所部署在的服务器。



    fastDFS优点:1.高可靠性:无单点故障 2.高吞吐性:只要 Group 足够多,数据流量将足够分散。

    搭建fastDFS(阿里服务器)

    安装 libfastcommon 和 FastDFS

    一、安装 libfastcommon


    这里是通过wget下载:

    wget https://github.com/happyfish100/libfastcommon/archive/V1.0.7.tar.gz
    

    解压 libfastcommon,命令:

    tar -zxvf V1.0.7.tar.gz
    

    移动解压目录到/usr/local下:

    mv libfastcommon-1.0.7 /usr/local/
    

    进入libfastcommon-1.0.7目录,命令:

    cd /usr/local/libfastcommon-1.0.7
    

    编译

    ./make.sh
    

    安装:

    ./make.sh install
    

    安装 libfastcommon 成功:


    #### 二、安装 FastDFS ---- 这里也是通过wget下载: ```java wget https://github.com/happyfish100/fastdfs/archive/V5.05.tar.gz ``` 解压 FastDFS: ```java tar -zxvf V5.05.tar.gz ``` 移动解压目录到/usr/local下: ```java mv fastdfs-5.05/ /usr/local/ ``` 进入fastfds-5.05目录,命令: ```java cd /usr/local/fastdfs-5.05 ``` 编译: ```java ./make.sh ``` 安装: ```java ./make.sh install ``` 安装成功: ![](https://img2020.cnblogs.com/blog/2026387/202012/2026387-20201209155332130-1517220266.jpg)

    配置

    配置 Tracker 服务


    上述安装成功后,在/etc/目录下会有一个fdfs的目录,进入它。会看到三个.sample后缀的文件,这是作者给我们的示例文件,我们需要把其中的tracker.conf.sample文件改为tracker.conf配置文件并修改它。参照以下命令:

    cd /etc/fdfs/
    cp tracker.conf.sample tracker.conf
    vim tracker.conf
    
    // 编辑tracker.conf文件,找到你需要修改的两个参数就可以了,我这里不做修改,用默认的
    # the base path to store data and log files // 数据和日志的存放目录
    base_path=/home/yuqing/fastdfs
    
    # HTTP port on this tracker server // http服务端口
    http.server_port=8070
    

    修改完成后我们需要建立tracker的工作目录,不然启动报错(上面base_path配置的目录)

    mkdir -p /home/yuqing/fastdfs
    

    启动tracker服务:

    /usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf start
    

    查看监听:

    ps -ef|grep fdfs 
    #或
    netstat -lnpt|grep fdfs
    

    Tracker服务安装成功,启动成功:


    配置 Storage 服务


    现在开始配置 Storage 服务,由于我这是单机器测试,你把 Storage 服务放在多台服务器也是可以的,它有 Group(组)的概念,同一组内服务器互备同步,这里不再演示。直接开始配置,依然是进入/etc/fdfs的目录操作,首先进入它。会看到三个.sample后缀的文件,我们需要把其中的storage.conf.sample文件改为storage.conf配置文件并修改它。参照以下命令:

    cd /etc/fdfs/
    cp storage.conf.sample storage.conf
    vim storage.conf
    

    打开storage.conf文件后,找到下面参数进行修改:

    # the base path to store data and log files // 数据和日志的存放目录
    base_path=/home/yuqing/fastdfs
    
    # path(disk or mount point) count, default value is 1 // storage在存储文件时支持多路径,默认只设置一个(下面store_path0配置的数量个数)
    store_path_count=1
    
    # store_path#, based 0, if store_path0 not exists, it's value is base_path // 配置多个store_path路径,从0开始,如果store_path0不存在,则base_path必须存在
    # the paths must be exist
    // 此处可以配置多个路径,如:store_path0=xx, store_path1=xx,store_path2=xx
    // 多个项目的文件按照文件夹分割,通过/usr/bin/fdfs_upload_file <config_file> <local_filename> [storage_ip:port] [store_path_index] 选择storage服务器和store_path角标上传到相应的项目下
    store_path0=/home/yuqing/fastdfs
    
    # tracker_server can ocur more than once, and tracker_server format is // 设置tracker_server换成自己的IP端口
    #  "host:port", host can be hostname or ip address
    tracker_server=192.168.2.121:22122
    

    修改完成后我们需要建立tracker的工作目录,不然启动报错(上面base_path配置的目录和store_path0配置的目录)

    mkdir -p /home/yuqing/fastdfs
    

    启动storage服务:

    /usr/bin/fdfs_storaged /etc/fdfs/storage.conf start
    

    查看监听:

    ps -ef|grep fdfs 或 netstat -lnpt|grep fdfs
    

    22122 和 23000端口都在监听:(/home/yuqing/fastdfs文件夹下看的话,会出现一大堆文件夹)



    到这里,我们安装配置并启动了 Tracker 和 Storage 服务,也没有报错了。那他俩是不是在通信呢?我们可以监视一下:

    /usr/bin/fdfs_monitor /etc/fdfs/storage.conf
    

    红线处ACTIVE成功:

    配置 fdfs_upload_file 上传文件

    依然是进入/etc/fdfs的目录操作,首先进入它。会看到三个.sample后缀的文件,我们需要把其中的client.conf.sample文件改为client.conf配置文件并修改它。参照以下命令:

    cd /etc/fdfs/
    cp client.conf.sample client.conf
    vim client.conf
    

    进行修改:

    # the base path to store log files // 日志的存放目录
    base_path=/home/yuqing/fastdfs
    
    # tracker_server can ocur more than once, and tracker_server format is // tracker_server服务的地址和端口号
    #  "host:port", host can be hostname or ip address
    tracker_server=192.168.2.121:22122
    

    修改完成后我们就可以通过/usr/bin/fdfs_upload_file命令上传文件了,/usr/bin/fdfs_upload_file 命令用法:

    [root@localhost fdfs]# /usr/bin/fdfs_upload_file
    Usage: /usr/bin/fdfs_upload_file <config_file> <local_filename> [storage_ip:port] [store_path_index]
    // 命令 配置文件 上传的文件 storage_ip和端口 store_path角标
    

    上传成功:


    附带 tracker.conf 和 storage.conf 的具体配置注释

    tracker.conf 配置文件分析:

    
    // 配置tracker.conf这个配置文件是否生效,因为在启动fastdfs服务端进程时需要指定配置文件,所以需要使次配置文件生效。false是生效,true是屏蔽。
    disabled=false
    
    // 程序的监听地址,如果不设定则监听所有地址
    bind_addr=
    
    // tracker监听的端口
    port=22122
    
    // 链接超时设定
    connect_timeout=30
    
    // tracker在通过网络发送接收数据的超时时间
    network_timeout=60
    
    // 数据和日志的存放地点
    base_path=/opt/fdfs
    
    // 服务所支持的最大链接数
    max_connections=256
    
    // 工作线程数一般为cpu个数
    work_threads=4
    
    // 在存储文件时选择group的策略,0:轮训策略 1:指定某一个组 2:负载均衡,选择空闲空间最大的group
    store_lookup=2
    
    // 如果上面的store_lookup选择了1,则这里需要指定一个group
    // store_group=group2
    
    // 在group中的哪台storage做主storage,当一个文件上传到主storage后,就由这台机器同步文件到group内的其他storage上,0:轮训策略 1:根据ip地址排序,第一个 2:根据优先级排序,第一个
    store_server=0
    
    // 选择那个storage作为主下载服务器,0:轮训策略 1:主上传storage作为主下载服务器
    download_server=0
    
    // 选择文件上传到storage中的哪个(目录/挂载点),storage可以有多个存放文件的base path 0:轮训策略 2:负载均衡,选择空闲空间最大的
    store_path=0
    
    // 系统预留空间,当一个group中的任何storage的剩余空间小于定义的值,整个group就不能上传文件了
    reserved_storage_space = 4GB
    
    // 日志信息级别
    log_level=info
    
    // 进程以那个用户/用户组运行,不指定默认是当前用户
    run_by_group=
    run_by_user=
    
    // 允许那些机器连接tracker默认是所有机器
    allow_hosts=*
    
    // 设置日志信息刷新到disk的频率,默认10s
    sync_log_buff_interval = 10
    
    // 检测storage服务器的间隔时间,storage定期主动向tracker发送心跳,如果在指定的时间没收到信号,tracker人为storage故障,默认120s
    check_active_interval = 120
    
    // 线程栈的大小,最小64K
    thread_stack_size = 64KB
    
    // storage的ip改变后服务端是否自动调整,storage进程重启时才自动调整
    storage_ip_changed_auto_adjust = true
    
    // storage之间同步文件的最大延迟,默认1天
    storage_sync_file_max_delay = 86400
    
    // 同步一个文件所花费的最大时间
    storage_sync_file_max_time = 300
    
    // 是否用一个trunk文件存储多个小文件
    use_trunk_file = false
    
    // 最小的solt大小,应该小于4KB,默认256bytes
    slot_min_size = 256
    
    // 最大的solt大小,如果上传的文件小于默认值,则上传文件被放入trunk文件中
    slot_max_size = 16MB
    
    // trunk文件的默认大小,应该大于4M
    trunk_file_size = 64MB
    
    // http服务是否生效,默认不生效
    http.disabled=false
    
    // http服务端口
    http.server_port=8080
    
    // 检测storage上http服务的时间间隔,<=0表示不检测
    http.check_alive_interval=30
    
    // 检测storage上http服务时所用请求的类型,tcp只检测是否可以连接,http必须返回200
    http.check_alive_type=tcp
    
    // 通过url检测storage http服务状态
    http.check_alive_uri=/status.html
    
    // if need find content type from file extension name
    http.need_find_content_type=true
    
    // 用include包含进http的其他设置
    // include http.conf
    

    storage.conf配置文件

    storage.conf配置文件分析:
    
    // 同tracker.conf
    disabled=false
    
    // 这个storage服务器属于那个group
    group_name=group1
    
    // 同tracker.conf
    bind_addr=
    
    // 连接其他服务器时是否绑定地址,bind_addr配置时本参数才有效
    client_bind=true
    
    // 同tracker.conf
    port=23000
    connect_timeout=30
    network_timeout=60
    
    // 主动向tracker发送心跳检测的时间间隔
    heart_beat_interval=30
    
    // 主动向tracker发送磁盘使用率的时间间隔
    stat_report_interval=60
    
    // 同tracker.conf
    base_path=/opt/fdfs
    max_connections=256
    
    // 接收/发送数据的buff大小,必须大于8KB
    buff_size = 256KB
    
    // 同tracker.conf
    work_threads=4
    
    // 磁盘IO是否读写分离
    disk_rw_separated = true
    
    // 是否直接读写文件,默认关闭
    disk_rw_direct = false
    
    // 混合读写时的读写线程数
    disk_reader_threads = 1
    disk_writer_threads = 1
    
    // 同步文件时如果binlog没有要同步的文件,则延迟多少毫秒后重新读取,0表示不延迟
    sync_wait_msec=50
    
    // 同步完一个文件后间隔多少毫秒同步下一个文件,0表示不休息直接同步
    sync_interval=0
    
    // 表示这段时间内同步文件
    sync_start_time=00:00
    sync_end_time=23:59
    
    // 同步完多少文件后写mark标记
    write_mark_file_freq=500
    
    // storage在存储文件时支持多路径,默认只设置一个
    store_path_count=1
    
    // 配置多个store_path路径,从0开始,如果store_path0不存在,则base_path必须存在
    store_path0=/opt/fdfs
    // store_path1=/opt/fastdfs2
    
    // subdir_count  * subdir_count个目录会在store_path下创建,采用两级存储
    subdir_count_per_path=256
    
    // 设置tracker_server
    tracker_server=x.x.x.x:22122
    
    // 同tracker.conf
    log_level=info
    run_by_group=
    run_by_user=
    allow_hosts=*
    
    // 文件在数据目录下的存放策略,0:轮训 1:随机
    file_distribute_path_mode=0
    
    // 当问及是轮训存放时,一个目录下可存放的文件数目
    file_distribute_rotate_count=100
    
    // 写入多少字节后就开始同步,0表示不同步
    fsync_after_written_bytes=0
    
    // 刷新日志信息到disk的间隔
    sync_log_buff_interval=10
    
    // 同步storage的状态信息到disk的间隔
    sync_stat_file_interval=300
    
    // 线程栈大小
    thread_stack_size=512KB
    
    // 设置文件上传服务器的优先级,值越小越高
    upload_priority=10
    
    // 是否检测文件重复存在,1:检测 0:不检测
    check_file_duplicate=0
    
    // 当check_file_duplicate设置为1时,次值必须设置
    key_namespace=FastDFS
    
    // 与FastDHT建立连接的方式 0:短连接 1:长连接
    keep_alive=0
    
    // 同tracker.conf
    http.disabled=false
    http.domain_name=
    http.server_port=8888
    http.trunk_size=256KB
    http.need_find_content_type=true
    // include http.conf
    

    nginx简单整合fastDFS


    这里小编只是简单的整合,只是为了访问方便,
    配置nginx的配置文件,nginx.conf文件:

    server {
            listen       89;#监听89端口
            server_name  localhost;#ip
            
            location /group1/M00 {## 当访问89端口并且/group1/M00开头的请求,都有映射到fastDFS的存储数据区
             alias /usr/local/fastdfs-5.05/files_music/data;# 该地址就是文件存储的数据文件夹
            }
    }
    

    FastDFS项目中实现文件上传下载


    了解FastDFS的工作流程

    一、pom文件中加jar包

     <dependency>
      <groupId>net.oschina.zcx7878</groupId>
      <artifactId>fastdfs-client-java</artifactId>
      <version>1.27.0.0</version>
    </dependency>
    
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-io</artifactId>
      <version>1.3.2</version>
    </dependency>
    

    二、配置FastDFS基本信息:

    在项目的resource目录下面新建fdfs_client.conf配置文件,配置信息如下:

    connect_timeout = 60
    network_timeout = 60
    charset = UTF-8
    http.tracker_http_port = 8070//搭建时的fastDFS的根据配置
    tracker_server = 192.168.200.128:22122
    

    二、工具类

    封装文件的实体类

    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;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public byte[] getContent() {
            return content;
        }
    
        public void setContent(byte[] content) {
            this.content = content;
        }
    
        public String getExt() {
            return ext;
        }
    
        public void setExt(String ext) {
            this.ext = ext;
        }
    
        public String getMd5() {
            return md5;
        }
    
        public void setMd5(String md5) {
            this.md5 = md5;
        }
    
        public String getAuthor() {
            return author;
        }
    
        public void setAuthor(String author) {
            this.author = author;
        }
    }
    

    上传工具类(上传、下载和删除等等操作)

    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 Administrator
     * @date 2020/12/9 10:58
     **/
    
    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 1.文件的组名  2.文件的路径信息
         */
        public static String[] upload(FastDFSFile file) {
            logger.info("File Name: " + file.getName() + "File Length:" + file.getContent().length);
            //获取文件的作者
            NameValuePair[] meta_list = new NameValuePair[1];
            meta_list[0] = new NameValuePair("author", file.getAuthor());
    
            long startTime = System.currentTimeMillis();
            //接收返回数据
            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());
            }
            logger.info("upload_file time used:" + (System.currentTimeMillis() - startTime) + " ms");
            if (uploadResults == null && storageClient!=null) {
                logger.error("upload file fail, error code:" + storageClient.getErrorCode());
            }
            //获取组名
            String groupName = uploadResults[0];
            //获取文件存储路径
            String remoteFileName = uploadResults[1];
            logger.info("upload file successfully!!!" + "group_name:" + groupName + ", remoteFileName:" + " " + remoteFileName);
    
            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);
    
            logger.info("delete file successfully!!!" + i);
        }
    
        /***
         * 获取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()+"/";
            return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":89"+"/";//89为nginx监听的端口,有nginx代理监听映射到文件,进行访问
        }
    
        /***
         * 获取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;
        }
    }
    

    测试前端页面,提交表上

    <form action="/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="file">
        <input type="submit" value="上传">
    </form>
    

    测试后端控制台,上传文件

    @Controller
    public class UploadController {
        private static Logger logger = LoggerFactory.getLogger(UploadController.class);
     
     	@PostMapping("/upload")
        public String singleFileUpload(@RequestParam("file") MultipartFile file,
                                       RedirectAttributes redirectAttributes) {
            if (file.isEmpty()) {
                redirectAttributes.addFlashAttribute("message", "Please select a file to upload");
                return "redirect:uploadStatus";
            }
            try {
                // Get the file and save it somewhere
                String path=saveFile(file);
                redirectAttributes.addFlashAttribute("message",
                        "You successfully uploaded '" + file.getOriginalFilename() + "'");
                redirectAttributes.addFlashAttribute("path",
                        "file path url '" + path + "'");
            } catch (Exception e) {
                logger.error("upload file failed",e);
            }
            return "上传成功";
        }
    
    
        /*
         * fastdfs上传到服务器
         */
        public String saveFile(MultipartFile multipartFile) throws IOException {
            String[] fileAbsolutePath={};
            String fileName=multipartFile.getOriginalFilename();
            String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
            byte[] file_buff = null;
            InputStream inputStream=multipartFile.getInputStream();
            if(inputStream!=null){
                int len1 = inputStream.available();
                file_buff = new byte[len1];
                inputStream.read(file_buff);
            }
            inputStream.close();
            FastDFSFile file = new FastDFSFile(fileName, file_buff, ext);
            try {
                fileAbsolutePath = FastDFSClient.upload(file);  //upload to fastdfs
            } catch (Exception e) {
                logger.error("upload file Exception!",e);
            }
            if (fileAbsolutePath==null) {
                logger.error("upload file failed,please upload again!");
            }
            String path=FastDFSClient.getTrackerUrl()+fileAbsolutePath[0]+ "/"+fileAbsolutePath[1];
            logger.info("文件上传成功,地址:"+path);
            return path;
        }
    }
    

    访问:

    访问成功:


    结束语


    至此我们的文件存储系统就搭建完成啦。

  • 相关阅读:
    2021/3/16
    2021/3/15
    plist
    百度小程序更新管理
    uni-app v-for v-modal
    小程序中不能使用字符串模板吗
    条件编译
    百度app 和小程序版本关系
    uni-app 全局变量的几种实现方式
    canvas 换行处理
  • 原文地址:https://www.cnblogs.com/MrYuChen-Blog/p/14109185.html
Copyright © 2020-2023  润新知