手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用

书接上回

首先我们先改一下上一次创建好的子项目里的application文件,其中分别改一下对应的application的名字,以便后面介绍SpringCloud的时候Nacos注册名字方便寻找到子项目,还需要修改端口,其中端口修改分别如下:

api:9009 users:9008 image:9007 gateway:88 audit:9001(gateway和audit后面会介绍到,可以先按照之前的先创建好)

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第1张图片

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第2张图片

测试接口

先介绍一下各个注解:

@Controller:返回给前端的内容是视图

@ResponseBody:将方法或者接口返回的数据渲染成JSON

@RestController:相当于@Controller+@ResponseBody,一般在类上注释了@RestController则就直接说明返回给前端的内容是JSON形式的,而如果只是单独需要某个方法返回JSON则可以接口注释@Controller而具体某个方法上注释@ResponseBody

@RequestParam:表示接受对应的请求里需要带有单个参数

@RequestBody:表示接受对应的请求里需要带有请求体

接下来在在cloud-photo-users项目里的UserController进行接口测试

1.先创建一个common包导入下面代码,作用是把返回的数据封装一下

BaseErrorInfo
package com.cloud.photo.users.common;

/**
 * 自定义描述错误接口
 * @author linzsh
 */
public interface BaseErrorInfo {
    /**
     * 错误码
     * @return 错误码
     */
    Integer getResultCode();

    /**
     * 错误描述
     * @return 错误描述
     */
    String getResultMsg();
}
CommonEnum:封装常用的自定义错误枚举类
package com.cloud.photo.users.common;

/**
 * 自定义错误枚举类
 * @author linzsh
 */
public enum CommonEnum implements BaseErrorInfo {

    //success
    SUCCESS(200, "成功!"),

    //error
    INTERNAL_SERVER_ERROR(90500, "服务器内部错误!"),

    SAVE_ERROR(90401, "插入失败!"),
    LOGIN_INFO_IS_NULL(90400, "登录信息为空!"),
    USERNAME_PASSWORD_ERROR(90400, "请检查账密是否正确!"),
    PHONE_IS_NULL(90400, "手机号不能为空!"),
    PHONE_IS_NOT_VALID(90400, "手机号不合法!"),
    FILE_LIST_IS_NULL(90400, "上传的文件列表未空!"),
    TRANS_IS_NULL(90404, "传输列表为空!"),
    LOGIN_FAIL(90400, "登录失败!"),
    USER_INFO_NOT_INPUT(90401, "你是第一次登录, 请补全用户信息!"),
    NO_LOGIN(90402, "您未登录!"),
    USER_IS_NULL(90404, "用户信息不存在!"),
    UNAUTHORIZED_CODE(90402,"登录Token过期,请重新登陆!"),
    NO_PERMISSION_CODE(90406, "没有权限进行此操作!"),
    FILE_NOT_UPLOADED(90407, "文件未上传!"),
    FILE_UPLOADED_ERROR(90408, "文件上传出错!"),
    NO_UPLOAD_FILE(90409, "没有获取到传输列表!"),
    ;

    //数据操作错误定义
    /** 错误码 */
    private final Integer resultCode;

    /** 错误描述 */
    private final String resultMsg;

    CommonEnum(Integer resultCode, String resultMsg) {
        this.resultCode = resultCode;
        this.resultMsg = resultMsg;
    }

    @Override
    public Integer getResultCode() {
        return this.resultCode;
    }

    @Override
    public String getResultMsg() {
        return this.resultMsg;
    }
}
ResultBody:封装数据体
package com.cloud.photo.users.common;

import com.alibaba.fastjson.JSONObject;
import lombok.Data;

/**
 * 业务响应体
 *
 * @author linzsh
 */
@Data
public class ResultBody {
    /**
     * 响应业务代码
     */
    private Integer code;

    /**
     * 响应消息
     */
    private String message;

    /**
     * 响应结果
     */
    private Object data;

    /**
     * 请求ID
     */
    private String requestId;

    public ResultBody() {
    }

    public ResultBody(BaseErrorInfo errorInfo) {
        this.code = errorInfo.getResultCode();
        this.message = errorInfo.getResultMsg();
    }

    /**
     * 返回默认成功
     *
     * @return ResultBody
     */
    public static ResultBody success() {
        return success(null, CommonEnum.SUCCESS);
    }

    /**
     * 成功
     *
     * @param data 数据响应体
     * @return ResultBody
     */
    public static ResultBody success(Object data,String requestId) {
        ResultBody rb = new ResultBody();
        rb.setCode(CommonEnum.SUCCESS.getResultCode());
        rb.setMessage(CommonEnum.SUCCESS.getResultMsg());
        rb.setData(data);
        rb.setRequestId(requestId);
        return rb;
    }

    /**
     * 成功
     *
     * @param data 数据响应体
     * @return ResultBody
     */
    public static ResultBody success(Object data) {
        ResultBody rb = new ResultBody();
        rb.setCode(CommonEnum.SUCCESS.getResultCode());
        rb.setMessage(CommonEnum.SUCCESS.getResultMsg());
        rb.setData(data);
        return rb;
    }

    /**
     * 成功
     *
     * @param data 数据体
     * @param errorInfo 一个业务状态枚举
     * @return ResultBody
     */
    public static ResultBody success(Object data, BaseErrorInfo errorInfo) {
        ResultBody rb = new ResultBody();
        if (errorInfo == null) {
            rb.setCode(CommonEnum.SUCCESS.getResultCode());
            rb.setMessage(CommonEnum.SUCCESS.getResultMsg());
        } else {
            rb.setCode(errorInfo.getResultCode());
            rb.setMessage(errorInfo.getResultMsg());
        }
        rb.setData(data);
        return rb;
    }

    /**
     * 失败
     * @param errorInfo 状态信息
     * @return ResultBody
     */
    public static ResultBody error(BaseErrorInfo errorInfo) {
        ResultBody rb = new ResultBody();
        rb.setCode(errorInfo.getResultCode());
        rb.setMessage(errorInfo.getResultMsg());
        rb.setData(null);
        return rb;
    }

    /**
     * 失败
     * @param code 状态码
     * @param message 状态描述
     * @return ResultBody
     */
    public static ResultBody error(Integer code, String message,String requestId) {
        ResultBody rb = new ResultBody();
        rb.setCode(code);
        rb.setMessage(message);
        rb.setData(null);
        rb.setRequestId(requestId);
        return rb;
    }


    /**
     * 失败
     * @param message 状态描述
     * @return ResultBody
     */
    public static ResultBody error(String message) {
        ResultBody rb = new ResultBody();
        rb.setCode(CommonEnum.INTERNAL_SERVER_ERROR.getResultCode());
        rb.setMessage(message);
        rb.setData(null);
        return rb;
    }

    /**
     * 失败
     * @param code 状态码
     * @param message 状态描述
     * @return ResultBody
     */
    public static ResultBody error(Integer code, String message) {
        ResultBody rb = new ResultBody();
        rb.setCode(code);
        rb.setMessage(message);
        rb.setData(null);
        return rb;
    }

    @Override
    public String toString() {
        return JSONObject.toJSONString(this);
    }

}

 2.写一个getUserInfo方法用来返回用户的手机信息

package com.cloud.photo.users.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cloud.photo.users.common.CommonEnum;
import com.cloud.photo.users.common.ResultBody;
import com.cloud.photo.users.entity.User;
import com.cloud.photo.users.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Controller;

/**
 * 

* 前端控制器 *

* * @author ltao * @since 2023-07-09 */ @RestController @RequestMapping("/users") @Slf4j public class UserController { @Autowired private UserService userService; /** * 获得用户信息,根据用户手机获取用户信息 * @param phone * @return resultBody */ @GetMapping("/getUserInfo") public ResultBody getUserInfo(@RequestParam(value = "phone") String phone){ log.info("getUserInfo-phone"+ phone + ",start!"); User user = userService.getOne(new QueryWrapper().eq("phone",phone)); log.info("getUserInfo()-phone=" + phone + ",user=" + user); ResultBody resultBody = (user == null ) ? ResultBody.error(CommonEnum.USER_IS_NULL) : ResultBody.success(user); log.info("getUserInfo()-phone=" + phone + ",resultBody=" + resultBody); return resultBody; } /** * 添加用户 * @param user 需要一个请求体,发送请求的时候可以根据User来构造一个 * @return */ @PostMapping("/addUser") public ResultBody addUser(@RequestBody User user){ boolean result = userService.save(user); return result ? ResultBody.success() : ResultBody.error(CommonEnum.SAVE_ERROR); } }

3.用postman或者自己选用其他方法进行测试接口 

3.1先根据User的数据类型插入一条信息,根据User结果编写json的请求体

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第3张图片

 可以看到插入成功

 手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第4张图片

注意:中途我出现了 mybatis-plus出现Invalid bound statement (not found) com.atguigu.admin.service.UserService.getBaseMappe的报错

解决:这里的MapperScan要详细,不能是.*这样泛 

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第5张图片

3.2 接下来测试查询方法

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第6张图片

成功发送后可以看到

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第7张图片

Nacos

Nacos在微服务中的作用是

  • 将子服务或者子项目在上面进行一个注册,相当于一个通讯录,方便其他服务需要调用相关信息的时候找到对应服务

官网地址:Nacos 快速开始

下载地址:Releases · alibaba/nacos (github.com)

 找到自己想要的版本 (官方推荐2.1.1,但具体看当时的官方文档推荐)

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第8张图片

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第9张图片

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第10张图片

1. 解压nacos
手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第11张图片

2. 修改配置
进入/conf目录,用文本编辑器打开application.properties文件
#启用登录校验
nacos.core.auth.enabled=true
#设置账号密码
nacos.core.auth.server.identity.key=nacos
nacos.core.auth.server.identity.value=nacos
#修改secretKey(这个不做强制要求,看你具体情况)
nacos.core.auth.plugin.nacos.token.secret.key=CloudPhoto0123456789012345678901234567890123 45678901234567890123456789
手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第12张图片

3. 进入/bin目录,执行命令启动nacos,standalone表示单机启动
startup.cmd -m standalone
手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第13张图片

修改集群变成单机

bin文件夹下,修改startup.cmd里面的内容 ,找到set MODE="cluster",替换为set MODE="standalone",原来默认的是集群,现在修改为单机模式

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第14张图片

4.复制 给出的网页然后复制粘贴到浏览器去访问,成功则会出现下面界面

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第15张图片

5.在IDEA里集成nacos

导入依赖 

        
        
            com.alibaba.nacos
            nacos-client
            2.1.1
        
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-config
        

yml配置 ,addr要根据启动后的nacos再来修改

nacos:
      discovery:
        username: nacos
        password: nacos
        server-addr: 127.0.0.1:8848

 手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第16张图片

 要记得也要去启动类的上面配置@EnableDiscoveryClient注解

 ​​​​​​手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第17张图片

启动过程这里会有一个红色的报错可以不用去理会他,因为服务一启动就会去nacos读配置,但如果没有数据的话回去微服务里的yml中寻找配置信息。

 如果启动的过程遇到了以下的报错可以在application name下面配置下面信息,意思是把web-application-type这个不启动,因为现在是发生了冲突

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第18张图片

 

 项目启动成功后去nacos查看相关微服务已经被我们注册上来了 

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第19张图片

接下来把其他服务也启动然后注册进来 

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第20张图片

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第21张图片

6.关闭(Windows下),意思就是直接关闭cmd窗口也可以

cmd shutdown.cmd

 

Fegin

是一种负载均衡的HTTP客户端, 使用Feign调用API就像调用本地方法一样,从避免了 调用目标微服务时,需要不断的解析/封装json 数据的繁琐

1.pom添加依赖

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第22张图片

        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
            2.1.3.RELEASE
        

2.在启动类上加入注解,表示feign接口扫描,

@EnableFeignClients(basePackages = {"com.cloud.photo.api.feign"})

3.测试

接下来在api项目中调用user服务中查询客户信息的测试。

大致流程思路:

3.1.在api子项目中创建一个接口类Api2UserFeginService,并在类上注释@FeignClient("cloud-photo-user"),表示这个调用了这个接口类的方法后就要去该cloud-photo-user项目下去寻找对应的方法具体实现,就像调用本地方法一样

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第23张图片

 手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第24张图片

3.2然后再编写一个Api2UserController,这是一个专属于Controller的方法,通过专属的api然后在里面调用 Api2UserFegin,Api2UserFegin再去寻找user子项目中具体的方法

 手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第25张图片

3.3同时在api项目的application.yml文件里配置上,Feign的负载均衡底层用的就是Ribbon,所以这里的请求超时配置其实就是配置Ribbon

# feign 超时配置
feign:
  client:
    config:
      default:
        # 超时设置 30 秒超时
        connectTimeout: 30000 #请求连接超时时间
        readTimeout: 30000 #请求处理的超时时间

3.4启动nacos,和启动其他服务,将服务注册到nacos上,再打开redis。这里我只启动了users和api服务,然后发送请求

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第26张图片

发送成功后结果如下: 

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第27张图片

 

 

 

 

总体思路:在一个服务如api想调用另一个服务如users时候要通过nacos先把服务注册到一个类似通讯录的地方然后通过fegin进行转发。如在api中写一个api/getUserInfo的Controller,再写一个fegin接口,里面调用user的接口进行转发,然后请求发到api上的时候再通过fegin把请求丢给user去处理

注意!!!!!:

1.报错:No qualifying bean of type ‘org.springframework.boot.autoconfigure.http.HttpMessageConverters‘ available

 No qualifying bean of type ‘org.springframework.boot.autoconfigure.http.HttpMessageConverters‘ avail_kkoneone11的博客-CSDN博客

2.报错:Could not autowire. No beans of ‘Api2UserFegin‘ type found

使用feign调用的时候出现Could not autowire. No beans of ‘Api2UserFegin‘ type found报错_kkoneone11的博客-CSDN博客

Api2UserFegin

package com.cloud.photo.api.feign;

import com.cloud.photo.api.common.ResultBody;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * @Author:kkoneone11
 * @name:Api2UserFeginService
 * @Date:2023/7/12 10:53
 */


/**
 * @FeignClient 表明要去寻找的项目的名字
 */

@FeignClient("cloud-photo-users")
@RequestMapping("/users")
public interface Api2UserFegin {

    /**
     * 调用use服务下的接口,注意接口调用要对应到具体的方法
     * @param phone
     * @return
     */
    @GetMapping("/getUserInfo")
    ResultBody getUserInfo(@RequestParam(value = "phone") String phone);
}

Api2UserController

package com.cloud.photo.api.controller;

import com.cloud.photo.api.common.ResultBody;
import com.cloud.photo.api.feign.Api2UserFegin;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author:kkoneone11
 * @name:Api2UserController
 * @Date:2023/7/12 10:53
 */

/**
 * 通过"/api/getUserInfo"路径去访问Api2UserController
 */
@RestController
@Slf4j
@RequestMapping("/api")
public class Api2UserController {
    @Autowired
    private Api2UserFegin api2UserFegin;


    @GetMapping("/getUserInfo")
    public ResultBody getUserInfo(@RequestParam(value = "phone") String phone){
        log.info("getUserInfo() - phone =" + phone + ",start!");
        //使用fegin调用
        ResultBody resultBody = api2UserFegin.getUserInfo(phone);
        log.info("getUserInfo() - phone" + phone + ",resultBody" + resultBody);
        return resultBody;
    }
}

SpringCloudGateway

通过gateway将请求

1.相关配置

1.1导入依赖

        
        
            org.springframework.cloud
            spring-cloud-starter-gateway
        

1.2SpringCloudGateway的yml配置

解析一下gateway中第一段id的含义:

  • id:指明这次路由的名字
  • uri: lb意思是负载均衡,如果nacos里找到5个则会负载到其中的一个,而cloud-photo-api是最终要去的服务。从注册中心获取
  • predicates:断言,就是发送的请求带有Path=/api/*这种前缀的就要去到上面的loud-photo-api
  • filters:过滤器:这里是重写路径,通过正则表达式修改。这里的意思是把/api/前面的路径都删除只保留后面的,如localhost:88/api/getUserInfo --> localhost:9009/getUserInfo
  • order,用于多个 Route 之间的排序,数值越小排序越靠前,匹配优先级越高。

这里执行顺序是:当发送一个请求过来的时候断言拦截到,然后交给filter处理完再丢回给uri

spring:
  cloud:
    #  网关配置
    gateway:
      routes:
        #   高优先级路由放前面
        - id: api_route
          uri: lb://cloud-photo-api
          predicates:
            - Path=/api/**
          #  localhost:88/api/** --> localhost:10000/**
          filters:
          # - RewritePath=/api/(?/?.*), /$\{segement}
        - id: audit_route
          uri: lb://cloud-photo-audit
          predicates:
            - Path=/audit/**
          #   localhost:88/audit/** --> localhost:10000/**
          filters:
            - RewritePath=/audit/(?/?.*), /$\{segement}
    # nacos config
    nacos:
      discovery:
        username: nacos
        password: nacos
        server-addr: 192.168.252.1:8848
  redis:
    host: 127.0.0.1
    port: 6379

  application:
    name: cloud-photo-gateway
  main:
    web-application-type: reactive
  datasource:
    url: jdbc:mysql://localhost:3306/photo?useSSL=false&characterEncoding=UTF-8&allowPublicKeyRetrieval=true
    username: root
    password: 1234
    driver-class-name: com.mysql.jdbc.Driver

  kafka:
    bootstrap-servers: 127.0.0.1:9092
    producer: # producer 生产者
      retries: 0 # 重试次数
      acks: 1 # 应答级别:多少个分区副本备份完成时向生产者发送ack确认(可选0、1、all/-1)
      batch-size: 16384 # 批量大小
      buffer-memory: 33554432 # 生产端缓冲区大小
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
    consumer: # consumer消费者
      group-id: javagroup # 默认的消费组ID
      enable-auto-commit: true # 是否自动提交offset
      auto-commit-interval: 100  # 提交offset延时(接收到消息后多久提交offset)

      # earliest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
      # latest:当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据
      # none:topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常
      auto-offset-reset: latest
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
#服务端口
server:
  port: 88

1.3.MyCorsConfiguration跨域类

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第28张图片

package com.photo.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

/**
 * 跨域配置
 * @author linzsh
 */
@Configuration
public class MyCorsConfiguration {

    @Bean
    public CorsWebFilter corsWebFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();

        //1、配置跨域

        //允许哪些请求头头进行跨域
        corsConfiguration.addAllowedHeader("*");
        //允许哪些请求方式进行跨域
        corsConfiguration.addAllowedMethod("*");
        //允许哪些请求来源进行跨域
        corsConfiguration.addAllowedOrigin("*");
        //允许携带cookies
        corsConfiguration.setAllowCredentials(true);

        source.registerCorsConfiguration("/**", corsConfiguration);

        return new CorsWebFilter(source);
    }
}

 手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第29张图片

 2.测试

2.1启动gateway主类确保都在nacos注册了 

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第30张图片

2.2往网关里发一条请求,使请求远程调用user中的getUserInfo。这里是发送了一条http://localhost:88/api/getUserInfo?phone=123456

请求 http://localhost:88/api/getUserInfo?phone=123456,进入网关后在断言处判断,然后通过filter变成localhost:9009/getUserInfo再丢给了api服务中去处理,而Api2UserController接受到之后又调用了fegin进行请求转发到user中去调用对应Controller处理

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第31张图片

 

S3存储-minio 

介绍:

S3 Simple Storage Service 简单存储服务,对象存储在存储段(bucket) 中,S3对象存储是亚马逊AWS提供的一种云存储服务,它可以帮助用户存储 和检索任意数量的数据,而且具有高可用性、高可靠性和高扩展性等优点。 在S3对象存储中,数据以对象的形式存储,每个对象都有一个唯一的键( key) 和一个值( value ),并且可以通过HTTP或HTTPS协议进行访问
  • 高可用性/多副本:如果某个物理位置发生故障,S3对象存储会自动将数据 切换到其他可用的位置,从而保证数据的连续性和可用性。
  • 可扩展性:S3对象存储可以存储任意数量的数据,而且可以根据需要自动扩 展存储容量。
  • 多方式访问:包括REST AP1、AWS SDK、命令行工具等,以满足不同的应 用场景和需求。
  • bucket/endPoint/AK/SK

1.配置

1.1hutool工具类导入依赖,如果用的阿里云的话version可能不用这么高,不然导入不成功


    cn.hutool
    hutool-all
    5.7.4


        
            com.amazonaws
            aws-java-sdk-core
            1.12.487
        


        
            com.amazonaws
            aws-java-sdk-s3
            1.12.487
        

1.2导入S3Util工具类

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第32张图片

 按照自己的实际情况修改

  • accessKey:登录账号
  • secretKey:登录密码
  • bucketName:要操作的桶名
  • serviceEndpoint:minio或者华为云obs地址
package com.cloud.photo.api.utils;

import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson.JSONObject;
import com.amazonaws.HttpMethod;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.*;
import org.apache.commons.lang.StringUtils;
import org.apache.tomcat.util.buf.HexUtils;

import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.util.*;

import static org.bouncycastle.asn1.x500.style.RFC4519Style.cn;

public class S3Util {




    //本地minio或者华为云的账号密码
    private static String accessKey = "minioadmin";
    private static String secretKey = "minioadmin";
    private static String bucketName = "cloud-photo";
    //本地minio或者华为云的连接
    private static String serviceEndpoint = "http://127.0.0.1:9000";


    private static String containerId = "10001";

    /**
     * 获取上传地址
     * @param suffixName
     * @param fileMd5
     * @return
     */
    public static String getPutUploadUrl(String suffixName,String fileMd5) {

        //链接过期时间
        Date expiration = new Date();
        long expTimeMillis = expiration.getTime();
        expTimeMillis += 1000 * 60 * 10;
        expiration.setTime(expTimeMillis);
        String objectId = UUID.randomUUID().toString().replaceAll("-","");
        String base64Md5 = "";
        if(StringUtils.isNotBlank(suffixName)){
            objectId = objectId +"." + suffixName;
        }
        //建立S3客户端,获取上传地址
        AmazonS3 s3Client = getAmazonS3Client();
        GeneratePresignedUrlRequest generatePresignedUrlRequest =
                new GeneratePresignedUrlRequest(bucketName, objectId)
                        .withMethod(HttpMethod.PUT)
                        .withExpiration(expiration);
        if(StringUtils.isNotBlank(fileMd5)){
            base64Md5= Base64.encode(HexUtils.fromHexString(fileMd5));
            generatePresignedUrlRequest = generatePresignedUrlRequest.withContentMd5(base64Md5);
        }
        URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);

        JSONObject jsonObject =new JSONObject();
        jsonObject.put("objectId",objectId);
        jsonObject.put("url",url);
        jsonObject.put("containerId",containerId);
        jsonObject.put("base64Md5",base64Md5);
        return jsonObject.toJSONString();
    }

    /**
     * 获取文件资源池信息
     * @param objectId
     * @return
     */
    public static S3ObjectSummary getObjectInfo(String objectId){
        AmazonS3 s3Client = getAmazonS3Client();

        ListObjectsV2Request listObjectsRequest = new ListObjectsV2Request()
                .withBucketName(bucketName)
                .withPrefix(objectId);

        ListObjectsV2Result listing = s3Client.listObjectsV2(listObjectsRequest);
        List s3ObjectSummarieList = listing.getObjectSummaries();

        if(s3ObjectSummarieList!=null && s3ObjectSummarieList.size()>0){
            return s3ObjectSummarieList.get(0);
        }
        return null;
    }

    public static String getDownloadUrl(String containerId, String objectId){
        return getDownloadUrl(containerId, objectId,null);
    }

    public static String getDownloadUrl(String containerId, String objectId,String filename) {
        AmazonS3 s3Client = getAmazonS3Client();
        GeneratePresignedUrlRequest httpRequest = new GeneratePresignedUrlRequest(bucketName, objectId);
        //设置过期时间
        Date expiration = new Date();
        long expTimeMillis = expiration.getTime();
        expTimeMillis += 1000 * 60 * 10;
        expiration.setTime(expTimeMillis);
        httpRequest.setExpiration(expiration);

        if(StringUtils.isNotBlank(filename)){
            String responseContentDisposition = null;
            try {
                responseContentDisposition = "attachment;filename=" + URLEncoder.encode(filename, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            httpRequest.addRequestParameter("response-content-disposition", responseContentDisposition);
        }
        return s3Client.generatePresignedUrl(httpRequest).toString();

    }

    private static AmazonS3 getAmazonS3Client(){
        AwsClientBuilder.EndpointConfiguration endpointConfiguration =new AwsClientBuilder.EndpointConfiguration(serviceEndpoint,"");
        AWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
        AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
                .withCredentials(new ProfileCredentialsProvider())
                .withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
                .withEndpointConfiguration(endpointConfiguration)
                .build();
        return s3Client;
    }

    /**
     * 查询桶列表
     */
    public static void getBuckets() {
        AmazonS3 s3Client = getAmazonS3Client();
        List bucketList = s3Client.listBuckets();
        for (int i = 0; i < bucketList.size(); i++) {
            System.out.println(bucketList.get(i).getName());
        }
    }



    /**
     * 查询桶的文件
     */

    public static void listObjects() {
        AmazonS3 s3Client = getAmazonS3Client();
        ListObjectsRequest listObjectRequest = new ListObjectsRequest();
        ObjectListing objectListing = null;
        listObjectRequest.setBucketName(bucketName);
        // listObjectRequest.setPrefix("DATALAKE/LF02/");

        int j=0;
        int num =0;
        do {
            objectListing = s3Client.listObjects(listObjectRequest);
            List s3ObjectSummaries = objectListing.getObjectSummaries();
            if (s3ObjectSummaries != null && s3ObjectSummaries.size() > 0) {
                for (int i = 0; i < s3ObjectSummaries.size(); i++) {
                    num =j*1000+i;
                    System.out.println(num+"|"+j+"|"+i + "|" + s3ObjectSummaries.get(i).getKey() + "|" + s3ObjectSummaries.get(i).getETag());
                }
            }
            j++;
            listObjectRequest.setMarker(objectListing.getNextMarker());
        } while (objectListing.isTruncated());
    }

    /**
     * 生成下载地址
     */

    public static void  genDownloadUrl  (){
        AmazonS3 s3Client = getAmazonS3Client();
        //华为obs存储文件路径
        String key = "123.jpg";
        //一小时后
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.HOUR,1);
        Date expiration = calendar.getTime();
        //生成下载地址
        URL url = s3Client.generatePresignedUrl(bucketName,key,expiration);
        String downloadUrl = url.getProtocol()+"://"+url.getHost()+":9000"+url.getFile();

        System.out.println(downloadUrl);
    }

    /**
     * 一次上传
     */

    public static void putObjectTest(){
        AmazonS3 s3Client = getAmazonS3Client();
        String key = "12345789.jpg";
        File file = new File("D:/peixun/img/123.jpg");
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName,key,file);
        s3Client.putObject(putObjectRequest);
        System.out.println("putObjectTest success + src="+file.getName()+",obs dest ="+key);
    }

    /**
     * 分片上传
     */

    public static void multiPut()  {
        AmazonS3 s3Client = getAmazonS3Client();
        //初始化上传
        String key = "33333";
        InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(bucketName,key);
        InitiateMultipartUploadResult initiateMultipartUploadResult = s3Client.initiateMultipartUpload(initiateMultipartUploadRequest);
        if(initiateMultipartUploadResult!=null){
            String uploadId = initiateMultipartUploadResult.getUploadId();
            //上传文件
            UploadPartRequest uploadPartRequest = new UploadPartRequest().withUploadId(uploadId);
            uploadPartRequest.setUploadId(uploadId);
            uploadPartRequest.setBucketName(bucketName);
            uploadPartRequest.setKey(key);
            List partETags = new ArrayList<>();
            byte[] content = new byte[5*1024*1024];
            String filePath = "d:\\SQLyog-13.1.8-0.x64Trial.exe";
            File file = new File(filePath);
            try{
                FileInputStream fis = new FileInputStream(file);
                BufferedInputStream bis = new BufferedInputStream(fis);
                int readSize = 0;
                int partNum =1;
                while ((readSize = bis.read(content)) > 0) {
                    uploadPartRequest.setPartSize(readSize);
                    uploadPartRequest.setPartNumber(partNum);
                    uploadPartRequest.setInputStream(new ByteArrayInputStream(content));
                    partNum++;
                    UploadPartResult  uploadPartResult= s3Client.uploadPart(uploadPartRequest);
                    partETags.add(uploadPartResult.getPartETag());
                    System.out.println("uploadPartResult:"+uploadPartResult.getPartNumber()+"|"+uploadPartResult.getETag()+"|"+uploadPartResult.getServerSideEncryption()+"|"+uploadPartResult.getPartETag());
                }


            }catch (Exception e){
                e.printStackTrace();
            }


            //合并提交
            CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(bucketName, key, uploadId, partETags);
            s3Client.completeMultipartUpload(compRequest);
        }
    }

    /**
     * 刪除文件
     */

    public static void delObject(){
        AmazonS3 s3Client = getAmazonS3Client();

        String key = "123.jpg";
        s3Client.deleteObject(bucketName,key);

    }






    public static void main(String[] args) {


      //getBuckets();

        listObjects();

       //putObject();

       // multiPut();




      // genDownloadUrlTest();
       // delObject();

       //  System.out.println("uuuuu======"+url);



    }
}

2.测试

最下方有主方法可以直接进行每个方法的测试

2.1查看桶  getBucketsTest();

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第33张图片

2.2分片上传  multiPutTest(); 记得修改上传文件的路径

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第34张图片

 

2.3genDownloadUrlTest()获取上传路径

注意!!:遇到NoClassDefFoundError: org/joda/time/DateTimeZone报错可以在pom里添加一个

  
        
            joda-time
            joda-time
            2.10
        

本机下创建一个文件夹,里面创建四个文件相当于四个节点

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第35张图片

 

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第36张图片

打开cmd窗口,输入已下命令启动minio的服务。如果是四台虚拟机,则文件路径换成虚拟机的http访问路径即可

minio.exe server D:\software\minio\minio_data\data1 D:\software\minio\minio_data\data2 D:\software\minio\minio_data\data3 D:\software\minio\minio_data\data4

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第37张图片

 手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第38张图片

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第39张图片

 创建一个testbucket桶手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第40张图片

 随便查看一个节点可以看到已经有创建好的testbucket

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第41张图片

 上传文件

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第42张图片

S3存储-华为云OBS

手把手教你云相册项目简易开发 day2 Springboot+SpringCloud+Nacos+Fegin+SpringCloudGateway+S3存储-minio/华为云OBS相关配置和简单使用_第43张图片

 

你可能感兴趣的:(云开发项目,spring,boot,spring,cloud,华为云,redis,分布式,Nacos,Fegin)