• 我爱java系列之---【分布式文件存储-FastDFS】


    一、体系结构 

    二、上传流程

    什么是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 点击添加文件,最后发送即可。

    愿你走出半生,归来仍是少年!
  • 相关阅读:
    response.redirect on asp.net is a 302 jump action
    什么吃掉了我的硬盘?
    百度流量统计将会影响搜索的排名
    发邮件 python
    bottle template usage
    想提神,喝中药,咖啡可可没用的
    企业退信的常见问题?
    用UnixBench测试VPS性能 判别是否值得购买
    域名注册及免费空间and企业邮箱
    LNMP一键安装包是什么?
  • 原文地址:https://www.cnblogs.com/hujunwei/p/11341614.html
Copyright © 2020-2023  润新知