美文网首页
SAAS-HRM-day4(FastDFS)

SAAS-HRM-day4(FastDFS)

作者: 程序员Darker | 来源:发表于2019-09-30 08:22 被阅读0次

    1. 图片统一处理

    1.1 问题发现和解决方案

    1. 由于我这是微服务项目,所以存储图片不能像以前那个样子,所以我在这里要统一处理图片,所以要使用分布式文件系统。
    2. 统一处理图片的方案有两种:
      • 1)租用别人的:阿里云对象存储或七牛云
      • 2)自己搭建:hdfs,FastDfs等
    3. 我这里使用自己搭建FastDfs文件系统

    1.2 FastDFS

    1.2.1 Fastdfs介绍

    1. FastDFS 是用 c语言编写的一款开源的分布式文件系统。FastDFS 为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用 FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
    2. FastDFS 架构包括 Tracker server 和 Storage server。客户端请求 Tracker server 进行文件上传、下载,通过 Tracker server 调度最终由 Storage server 完成文件上传和下载。
    3. Tracker server 作用是负载均衡和调度,通过 Tracker server 在文件上传时可以根据一些策略找到 Storage server 提供文件上传服务。可以将 tracker 称为追踪服务器或调度服务器。
    4. Storage server作用是文件存储,客户端上传的文件最终存储在 Storage服务器上,Storageserver没有实现自己的文件系统而是利用操作系统的文件系统来管理文件。可以将storage称为存储服务器。
    5. 服务端的两个角色:
      • Tracker:管理集群,tracker 也可以实现集群。每个 tracker 节点地位平等。收集 Storage 集群的状态。
      • Storage:实际保存文件 Storage 分为多个组,每个组之间保存的文件是不同的。每个组内部可以有多个成员,组成员内部保存的内容是一样的,组成员的地位是一致的,没有主从的概念

    参考:

    1.2.2 Fastdfs上传下载流程

    客户端上传文件后存储服务器将文件 ID 返回给客户端,此文件 ID 用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。

    1. 组名文件上传后所在的 storage 组名称,在文件上传成功后有 storage 服务器返回,需要客户端自行保存。
    2. 虚拟磁盘路径:storage 配置的虚拟路径,与磁盘选项 store_path*对应。如果配置了store_path0则是M00,如果配置了 store_path1 则是 M01,以此类推。
    3. 数据两级目录:storage服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件。
    4. 文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储服务器IP地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息。
    5. 下载流程图
      [图片上传失败...(image-ce0149-1569802914948)]

    1.2.3 简单的FastDFS架构

    [图片上传失败...(image-ea69bf-1569802914948)]

    1.2.4 Fastdfs搭建

    运维人员已经搭建完毕!

    简单命令

    //启动两个FastDFS的服务
    service fdfs_trackerd start
    service fdfs_storaged start
    //
    /usr/bin/fdfs_monitor /etc/fdfs/storage.conf
    //提供web服务
    /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
    //上传一个进行测试
    fdfs_test /etc/fdfs/client.conf upload /root/install.log
    //检查防火墙状态和关闭防火墙。如果访问不了上传的文件,应该就是防火墙的问题
    service iptables stop/status
    

    1.2.5 入门

    需求:将本地图片上传至图片服务器,再控制台打印url

    1.2.5.1 步骤分析

    1. 创建Maven工程FastDFSTest
    2. 导包(FastDFS所需要的)
    3. 配置文件
    4. 创建java类
    5. 测试上传
    6. 测试浏览
    7. 下载和删除

    1.2.5.2 步骤实现

    1. 创建Maven工程FastDFSTest
    2. 导包(FastDFS所需要的)

    由于FastDFS客户端jar包并没有在中央仓库中,所以需要使用下列命令手动安装jar包到Maven本地仓库。

    安装jar包到本地仓库命令:

    mvn install:install-file -DgroupId=组名 -DartifactId=包名 -Dversion=版本 -Dpackaging=jar -Dfile=jar所在磁盘的绝对路径
    //下面举例:
    mvn install:install-file -DgroupId=org.csource -DartifactId=fastdfs-client -Dversion=1.2 -Dpackaging=jar -Dfile=D:\Java\resource\fastdfs-1.2.jar
    
            <dependency>
                <groupId>org.csource</groupId>
                <artifactId>fastdfs-client</artifactId>
                <version>1.2</version>
            </dependency>
    
    1. 配置文件
      在resources下创建个文件,名字为fdfs_client.conf,在里面配置对应的服务器ip和端口。
    tracker_server=172.16.6.63:22122
    
    1. 创建java类,用于测试
    package cn.wangningbo;
    
    import org.csource.fastdfs.*;
    
    public class FastdfsTest {
        public static void main(String[] args) throws Exception {
            // 1、加载配置文件,配置文件中的内容就是 tracker 服务的地址。
            ClientGlobal.init("D:/maven_work/fastDFS-demo/src/fdfs_client.conf");
            // 2、创建一个 TrackerClient 对象。直接 new 一个。
            TrackerClient trackerClient = new TrackerClient();
            // 3、使用 TrackerClient 对象创建连接,获得一个 TrackerServer 对象。
            TrackerServer trackerServer = trackerClient.getConnection();
            // 4、创建一个 StorageServer 的引用,值为 null
            StorageServer storageServer = null;
            // 5、创建一个 StorageClient 对象,需要两个参数 TrackerServer 对象、StorageServer 的引用
            StorageClient storageClient = new StorageClient(trackerServer, storageServer);
            // 6、使用 StorageClient 对象上传图片。
            //扩展名不带“.”
            String[] strings = storageClient.upload_file("D:/pic/benchi.jpg", "jpg",
                    null);
            // 7、返回数组。包含组名和图片的路径。
            for (String string : strings) {
                System.out.println(string);
            }
        }
    }
    
    1. 测试上传
      在main方法里面指定配置文件路径和图片路径,即可完成上传

    运行main方法,控制台输出结果

    group1
    M00/00/02/rBAGP11vi8SAcE4NAAAv6VpXil8827.png
    
    1. 测试上传结果浏览

    浏览器访问:http://172.16.6.63/group1/M00/00/02/rBAGP11vi8SAcE4NAAAv6VpXil8827.png

    显示图片,即可算作上传成功

    1. 下载和删除

    1.3 后台上传服务

    实现上传、下载、删除!

    1.3.1 模块结构

    在二级子模块hrm_basic_parent下创建三级子模块hrm_basic_fastdfs_interface和hrm_basic_fastdfs_service,名字为用来做图片统一处理。

    1.3.2 interface配置

    1.3.2.1 步骤分析

    1. 导包
    2. client准备

    1.3.2.2 步骤实现

    1. 导包
            <dependency>
                <groupId>cn.wangningbo.hrm</groupId>
                <artifactId>hrm_basic_util</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
            <!--不能直接依赖starter,有自动配置,而消费者是不需要额。-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <!--客户端feign支持-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
    
    1. client客户端准备

    FastDFSClient

    package cn.wangningbo.hrm.client;
    
    import cn.wangningbo.hrm.util.AjaxResult;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.cloud.openfeign.FeignClientsConfiguration;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.multipart.MultipartFile;
    
    /**
     * @author wangningbo
     * @since 2019-09-04
     */
    @FeignClient(value = "HRM-FASTDFS", configuration = FeignClientsConfiguration.class,
            fallbackFactory = FastDFSClientHystrixFallbackFactory.class)
    @RequestMapping("/fastdfs")
    public interface FastDFSClient {
        /**
         * 上传
         *
         * @param file
         * @return
         */
        @RequestMapping(value = "/upload", method = RequestMethod.POST)
        String upload(@RequestBody MultipartFile file);
    
        /**
         * 删除
         *
         * @param path
         * @return
         */
        @RequestMapping(value = "/delete", method = RequestMethod.DELETE)
        AjaxResult delete(@RequestParam("path") String path);
    
        /**
         * 下载
         *
         * @param path
         */
        @RequestMapping(value = "/download", method = RequestMethod.GET)
        void download(@RequestParam("path") String path);//直接把流写到response
    
    }
    

    FastDFSClientHystrixFallbackFactory

    package cn.wangningbo.hrm.client;
    
    import cn.wangningbo.hrm.util.AjaxResult;
    import feign.hystrix.FallbackFactory;
    import org.springframework.stereotype.Component;
    import org.springframework.web.multipart.MultipartFile;
    
    @Component
    public class FastDFSClientHystrixFallbackFactory implements FallbackFactory<FastDFSClient> {
        @Override
        public FastDFSClient create(Throwable throwable) {
            return new FastDFSClient() {
                @Override
                public String upload(MultipartFile file) {
                    return null;
                }
    
                @Override
                public AjaxResult delete(String path) {
                    return null;
                }
    
                @Override
                public void download(String path) {
    
                }
            };
        }
    }
    

    1.3.3 service配置

    1.3.3.1 步骤分析

    1. 导包
    2. 配置文件
    3. 入口类
    4. 日志
    5. 实现服务
      • (1)配置文件和工具类准备
      • (2)实现服务
    6. zuul配置路由
    7. swagger配置
      • (1)本项目配置swagger
      • (2)网关配置swagger
    8. 测试swagger
      • (1)swagger本项目测试
      • (2)swagger网关测试
    9. 测试上传和删除(postman)

    1.3.3.2 步骤实现

    1. 导包
            <dependency>
                <groupId>cn.wangningbo.hrm</groupId>
                <artifactId>hrm_basic_fastdfs_interface</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
            <!--web场景-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!--测试场景-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <!-- Eureka 客户端依赖 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <!--引入swagger支持-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.9.2</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.9.2</version>
            </dependency>
            <!--配置中心支持-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-config</artifactId>
            </dependency>
            <!--fastdfs包,自己安装-->
            <dependency>
                <groupId>org.csource</groupId>
                <artifactId>fastdfs-client</artifactId>
                <version>1.2</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>2.4</version>
            </dependency>
    
    1. 配置文件application.yml
    server:
      port: 9003 # 服务的端口
    spring:
      application:
        name: hrm-fastdfs # 应用的名字
    eureka:
      client: # 这个服务是eureka的客户端
        service-url: # eureka服务端的url
          defaultZone: http://localhost:7001/eureka # 注册到eureka服务端,服务端的地址
      instance:
        prefer-ip-address: true # 显示ip地址
    
    1. 入口类
    package cn.wangningbo.hrm;
    
    import cn.wangningbo.hrm.client.FastDFSClient;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    @SpringBootApplication
    @EnableEurekaClient
    public class FastDFS9003Application {
        public static void main(String[] args) {
            SpringApplication.run(FastDFS9003Application.class, args);
        }
    }
    
    1. 日志集成

    在resources下新建一个logback的配置文件,名字为logback-spring.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!--
    scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
    scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
    debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
    -->
    <configuration scan="false" scanPeriod="60 seconds" debug="false">
        <!-- 定义日志的根目录 -->
        <property name="LOG_HOME" value="/hrm/" />
        <!-- 定义日志文件名称 -->
        <property name="appName" value="hrm-fastdfs"></property>
        <!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
        <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
            <!--
            日志输出格式:
                %d表示日期时间,
                %thread表示线程名,
                %-5level:级别从左显示5个字符宽度
                %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 
                %msg:日志消息,
                %n是换行符
            -->
            <layout class="ch.qos.logback.classic.PatternLayout">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            </layout>
        </appender>
    
        <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->  
        <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <!-- 指定日志文件的名称
               /hrm/hrm-course/hrm-course.log
            -->
            <file>${LOG_HOME}/${appName}/${appName}.log</file>
            <!--
            当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
            TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
            -->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!--
                滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动 
                %i:当文件大小超过maxFileSize时,按照i进行文件滚动
                -->
                <fileNamePattern>${LOG_HOME}/${appName}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
                <!-- 
                可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,
                且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,
                那些为了归档而创建的目录也会被删除。
                -->
                <MaxHistory>365</MaxHistory>
                <!-- 
                当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy
                -->
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>100MB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
            </rollingPolicy>
            <!-- 日志输出格式: -->     
            <layout class="ch.qos.logback.classic.PatternLayout">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
            </layout>
        </appender>
    
        <!-- 
            logger主要用于存放日志对象,也可以定义日志类型、级别
            name:表示匹配的logger类型前缀,也就是包的前半部分
            level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
            additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,
            false:表示只用当前logger的appender-ref,true:
            表示当前logger的appender-ref和rootLogger的appender-ref都有效
        -->
        <!-- hibernate logger -->
        <logger name="cn.wangningbo" level="debug" />
        <!-- Spring framework logger -->
        <logger name="org.springframework" level="debug" additivity="false"></logger>
    
    
    
        <!-- 
        root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
        要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。 
        -->
        <root level="info">
            <appender-ref ref="stdout" />
            <appender-ref ref="appLogAppender" />
        </root>
    </configuration> 
    
    1. 实现服务
    • (1)配置文件和工具类准备

    配置文件fdfs_client.conf

    tracker_server=172.16.6.63:22122
    

    工具类

    package cn.wangningbo.hrm.util;
    
    import org.csource.common.NameValuePair;
    import org.csource.fastdfs.*;
    
    public class FastDfsApiOpr {
    
        //从classpath
        public static String CONF_FILENAME = FastDfsApiOpr.class.getClassLoader()
                .getResource("fdfs_client.conf").getFile();
    
    
        /**
         * 上传文件
         *
         * @param file
         * @param extName
         * @return
         */
        public static String upload(byte[] file, String extName) {
    
            try {
                ClientGlobal.init(CONF_FILENAME);
    
                TrackerClient tracker = new TrackerClient();
                TrackerServer trackerServer = tracker.getConnection();
                StorageServer storageServer = null;
    
                StorageClient storageClient = new StorageClient(trackerServer, storageServer);
                NameValuePair nvp[] = new NameValuePair[]{
                        new NameValuePair("age", "18"),
                        new NameValuePair("sex", "male")
                };
                String fileIds[] = storageClient.upload_file(file, extName, nvp);
    
                System.out.println(fileIds.length);
                System.out.println("组名:" + fileIds[0]);
                System.out.println("路径: " + fileIds[1]);
                return "/" + fileIds[0] + "/" + fileIds[1];
    
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        /**
         * 上传文件
         *
         * @param extName
         * @return
         */
        public static String upload(String path, String extName) {
    
            try {
                ClientGlobal.init(CONF_FILENAME);
    
                TrackerClient tracker = new TrackerClient();
                TrackerServer trackerServer = tracker.getConnection();
                StorageServer storageServer = null;
                StorageClient storageClient = new StorageClient(trackerServer, storageServer);
                String fileIds[] = storageClient.upload_file(path, extName, null);
    
                System.out.println(fileIds.length);
                System.out.println("组名:" + fileIds[0]);
                System.out.println("路径: " + fileIds[1]);
                return "/" + fileIds[0] + "/" + fileIds[1];
    
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        /**
         * 下载文件
         *
         * @param groupName
         * @param fileName
         * @return
         */
        public static byte[] download(String groupName, String fileName) {
            try {
    
                ClientGlobal.init(CONF_FILENAME);
    
                TrackerClient tracker = new TrackerClient();
                TrackerServer trackerServer = tracker.getConnection();
                StorageServer storageServer = null;
    
                StorageClient storageClient = new StorageClient(trackerServer, storageServer);
                byte[] b = storageClient.download_file(groupName, fileName);
                return b;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        /**
         * 删除文件
         *
         * @param groupName
         * @param fileName
         */
        public static void delete(String groupName, String fileName) {
            try {
                ClientGlobal.init(CONF_FILENAME);
    
                TrackerClient tracker = new TrackerClient();
                TrackerServer trackerServer = tracker.getConnection();
                StorageServer storageServer = null;
    
                StorageClient storageClient = new StorageClient(trackerServer,
                        storageServer);
                int i = storageClient.delete_file(groupName, fileName);
                System.out.println(i == 0 ? "删除成功" : "删除失败:" + i);
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("删除异常," + e.getMessage());
            }
        }
    }
    
    • (2)实现服务
    package cn.wangningbo.hrm.controller;
    
    import cn.wangningbo.hrm.util.AjaxResult;
    import cn.wangningbo.hrm.util.FastDfsApiOpr;
    import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;
    import org.apache.commons.io.IOUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.multipart.MultipartFile;
    import sun.nio.ch.IOUtil;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    @RestController
    @RequestMapping("/fastdfs")
    public class FastDFSController {
        // 使用slf4j的logger记录错误到文件里 在每处报错的地方使用error方法记录错误
        Logger logger = LoggerFactory.getLogger(FastDFSController.class);
    
        /**
         * 上传
         *
         * @param file
         * @return
         */
        @PostMapping("/upload")
        public String upload(@RequestParam("file") MultipartFile file) {
            try {
                //拿到上传的文件名
                String fileName = file.getOriginalFilename();
                //获取文件的后缀名
                String extName = fileName.substring(fileName.lastIndexOf(".") + 1);
                System.out.println(extName);
                //上传文件 文件的字节数组和后缀名
                return FastDfsApiOpr.upload(file.getBytes(), extName);
            } catch (Exception e) {
                e.printStackTrace();
                //记录错误到日志文件
                logger.error("upload_error..." + e.getMessage());
            }
            return null;
        }
    
        /**
         * 删除
         *
         * @param path
         * @return
         */
        @DeleteMapping("/delete")
        public AjaxResult delete(@RequestParam("path") String path) {
            //假设前台传过来是/group1/xxx
            try {
                //获取相对路径    // 去掉路径的第1个字符 //拿到的就是group1/xxx //
                String pathTemp = path.substring(1);
                // 获取组名     //以/进行分割,拿到分割后的第一块
                String groupName = pathTemp.substring(0, pathTemp.indexOf("/"));
                //indexOf("/)返回"/"在字符串中第一次出现处的索引
                //substring(数字n)去掉pathTemp这个字符串前面的n个字符
                String remotePath = pathTemp.substring(pathTemp.indexOf("/") + 1);
                System.out.println(groupName);
                System.out.println(remotePath);
                FastDfsApiOpr.delete(groupName, remotePath);
                return AjaxResult.me();
            } catch (Exception e) {
                e.printStackTrace();
                //记录错误到日志文件
                logger.error("delete_error..." + e.getMessage());
                return AjaxResult.me().setSuccess(false).setMessage(e.getMessage());
            }
        }
    
        /**
         * 下载
         *
         * @param path
         * @param response
         */
        @GetMapping("/download")
        public void download(@RequestParam("path") String path, HttpServletResponse response) {
            //获取相对路径    // 去掉路径的第1个字符 //拿到的就是group1/xxx //
            String pathTemp = path.substring(1);
            //获取第一个"/"之前的字符 //获取组名
            String groupName = pathTemp.substring(0, pathTemp.indexOf("/"));
            //indexOf("/)返回"/"在字符串中第一次出现处的索引
            //substring(数字n)去掉pathTemp这个字符串前面的n个字符
            String remotePath = pathTemp.substring(pathTemp.indexOf("/") + 1);
            System.out.println(groupName);
            System.out.println(remotePath);
            //注意关流
            OutputStream os = null;
            InputStream is = null;
            try {
                byte[] datas = FastDfsApiOpr.download(groupName, remotePath);
                os = response.getOutputStream();//直接以流的方式返回
                is = new ByteInputStream(datas, datas.length);
                IOUtils.copy(is, os);
            } catch (Exception e) {
                e.printStackTrace();
                logger.error("download_error..." + e.getMessage());
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (os != null) {
                    try {
                        os.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    1. zuul配置路由
    fastdfs.serviceId: hrm-fastdfs # 服务名
    fastdfs.path: /fastdfs/** # 把fastdfs打头的所有请求都转发给hrm-fastdfs
    
    server:
      port: 9527
    spring:
      application:
        name: zuul-gateway
    zuul:
      routes: 
        sysmanage.serviceId: hrm-sysmanage # 服务名
        sysmanage.path: /sysmanage/** # 把sysmanage打头的所有请求都转发给hrm-sysmanage
        course.serviceId: hrm-course # 服务名
        course.path: /course/** # 把course打头的所有请求都转发给hrm-course
        fastdfs.serviceId: hrm-fastdfs # 服务名
        fastdfs.path: /fastdfs/** # 把fastdfs打头的所有请求都转发给hrm-fastdfs
      ignored-services: "*" # 所有服务都不允许以服务名来访问
      prefix: "/services" # 加一个统一前缀
      retryable: true # 是否重试
    ribbon:
      ConnectTimeout: 250 # 连接超时时间(ms)
      ReadTimeout: 2000 # 通信超时时间(ms)
      OkToRetryOnAllOperations: true # 是否对所有操作重试
      MaxAutoRetriesNextServer: 2 # 同一服务不同实例的重试次数
      MaxAutoRetries: 1 # 同一实例的重试次数
    hystrix:
      command:
        default:
          execution:
            isolation:
              thread:
                timeoutInMillisecond: 3000 # 熔断超时时长:3000ms
    
    1. swagger配置
    • (1)本项目配置swagger

    导包

    <!--引入swagger支持-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.9.2</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.9.2</version>
            </dependency>
    

    本项目配置

    package cn.wangningbo.hrm.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.service.Contact;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    
    @Configuration
    @EnableSwagger2
    public class Swagger2 {
     
        @Bean
        public Docket createRestApi() {
            return new Docket(DocumentationType.SWAGGER_2)
                    .apiInfo(apiInfo())
                    .select()
                    //对外暴露服务的包,以controller的方式暴露,所以就是controller的包.
                    .apis(RequestHandlerSelectors.basePackage("cn.wangningbo.hrm.controller"))
                    .paths(PathSelectors.any())
                    .build();
        }
    
    
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .title("分布式文件系统api")
                    .description("分布式文件系统接口文档说明")
                    .contact(new Contact("wangningbo", "", "wang_ning_bo163@163.com"))
                    .version("1.0")
                    .build();
        }
    }
    
    • (2)网关zuul配置swagger
    resources.add(swaggerResource("分布式文件系统", "/services/fastdfs/v2/api-docs", "2.0"));
    
    package cn.wangningbo.hrm.config;
    
    import org.springframework.context.annotation.Primary;
    import org.springframework.stereotype.Component;
    import springfox.documentation.swagger.web.SwaggerResource;
    import springfox.documentation.swagger.web.SwaggerResourcesProvider;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @Component
    @Primary
    public class DocumentationConfig implements SwaggerResourcesProvider {
        @Override
        public List<SwaggerResource> get() {
            List resources = new ArrayList<>();
            //通过网关访问服务地址
            resources.add(swaggerResource("系统管理", "/services/sysmanage/v2/api-docs", "2.0"));
            resources.add(swaggerResource("课程中心", "/services/course/v2/api-docs", "2.0"));
            resources.add(swaggerResource("分布式文件系统", "/services/fastdfs/v2/api-docs", "2.0"));
            return resources;
        }
    
        private SwaggerResource swaggerResource(String name, String location, String version) {
            SwaggerResource swaggerResource = new SwaggerResource();
            swaggerResource.setName(name);
            swaggerResource.setLocation(location);
            swaggerResource.setSwaggerVersion(version);
            return swaggerResource;
        }
    }
    
    1. 测试swagger,启动项目

    2. 测试上传和删除(postman)

      postman测试文件上传:输入上传地址-->选择post方法-->Body-->form-data-->key那里输入文件的key,尾部选择file-->value那里会有个按钮,点击以后选择文件-->send发送请求

      返回结果:/group1/M00/00/02/rBAGP11vwsGAPpmLAA3_Ih_wmb8559.png

      浏览器访问上传到的服务器,172.16.6.63/group1/M00/00/02/rBAGP11vwsGAPpmLAA3_Ih_wmb8559.png

      可以看到图片

    1.3.4 整改前端

    略过

    2. 课程管理

    2.1 课程管理代码生成

    2.1.1 业务说明

    2.1.2 表设计

    2.1.3 代码生成

    2.2 课程管理crud

    2.2.1 分页列表

    分页+高级查询+关联查询

    2.2.1.1 步骤分析

    1. domain改造
    2. controller
    3. IService
    4. ServiceImpl
    5. mapper.java
    6. mapper.xml

    2.2.1.2 步骤实现(Course)

    1. domain改造
        @TableField(exist = false)
        private CourseType courseType; //课程类型
    
        @TableField(exist = false)
        private CourseDetail detail; //课程详情
    
    1. controller
        @RequestMapping(value = "/json", method = RequestMethod.POST)
        public PageList<Course> json(@RequestBody CourseQuery query) {
            return courseService.selectPageList(query);
        }
    
    1. IService
        //分页+高级查询+关联查询
        PageList<Course> selectPageList(CourseQuery query);
    
    1. ServiceImpl
        @Autowired
        private CourseMapper courseMapper;
    
        @Override
        public PageList<Course> selectPageList(CourseQuery query) {
            Page page = new Page<>(query.getPage(), query.getRows());
            List<Course> list = courseMapper.loadPageList(page, query);
            return new PageList<>(page.getTotal(), list);
        }
    
    1. mapper.java
        List<Course> loadPageList(Page page, @Param("query") CourseQuery query);
    
    1. mapper.xml
        <!--List<Course> loadPageList(Page page, @Param("query") CourseQuery query);-->
        <select id="loadPageList" parameterType="CourseQuery" resultMap="CourseMap">
            SELECT
            c.*, ct.id ct_id,
            ct.`name` ct_name
            FROM
            t_course c
            LEFT JOIN t_course_type ct ON c.course_type_id = ct.id
            <include refid="whereSql"></include>
        </select>
        <sql id="whereSql">
            <where>
                <if test="query.keyword!=null and query.keyword!=''">
                    c.name like concat("%",#{query.keyword},"%") or
                    c.users like concat('%',#{query.keyword},'%') or
                    c.tenantName like concat('%',#{query.keyword},'%') or
                    c.userName like concat('%',#{query.keyword},'%')
                </if>
            </where>
        </sql>
        <resultMap id="CourseMap" type="Course">
            <id column="id" property="id" />
            <result column="name" property="name" />
            <result column="users" property="users" />
            <result column="course_type_id" property="courseTypeId" />
            <result column="grade" property="grade" />
            <result column="status" property="status" />
            <result column="tenant_id" property="tenantId" />
            <result column="tenantName" property="tenantName" />
            <result column="user_id" property="userId" />
            <result column="userName" property="userName" />
            <result column="start_time" property="startTime" />
            <result column="end_time" property="endTime" />
            <association property="courseType" javaType="CourseType">
                <id column="ct_id" property="id" />
                <result column="ct_name" property="name" />
            </association>
        </resultMap>
    

    2.2.2 基本信息和详情增删改

    2.2.3 课程营销信息维护

    2.2.4 课程图片维护

    相关文章

      网友评论

          本文标题:SAAS-HRM-day4(FastDFS)

          本文链接:https://www.haomeiwen.com/subject/rakcpctx.html