谷粒商城-全栈-23 商品服务-阿里云存储 (OSS) 整合到第三方微服务
一、创建第三方微服务
由于OSS业务属于第三方服务,后期可能还会有短信、邮件服务,所以,为了方便管理,将OSS文件上传业务整合到第三方微服务,以后相关的功能都在这里扩展。
1、创建第三方微服务
右键File->Module->使用 Spring Intitializr 创建模块
- Project Metadata
- Group: com.atguigu.gulimall
- Artifact: gulimall-third-party
- Package: com.atguigu.gulimall.thirdparty
- Dependencies添加依赖
- spring web
- Spring Cloud openfeign
2、添加依赖
修改gulimall-third-party/pom.xml
添加依赖
<dependency>
<groupId>com.atguigu.gulimall</groupId>
<artifactId>gulimall-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>aliyun-oss-spring-boot-starter</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Aliyun Spring Boot dependencies -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>aliyun-spring-boot-dependencies</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
完整的 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.16.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.atguigu.gulimall</groupId>
<artifactId>gulimall-third-party</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gulimall-third-party</name>
<description>第三方服务</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR6</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>com.atguigu.gulimall</groupId>
<artifactId>gulimall-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>aliyun-oss-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Aliyun Spring Boot dependencies -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>aliyun-spring-boot-dependencies</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
如果加载不到 aliyun-spring-boot-dependencies
的相关依赖,需要检查 spring-cloud.version
配置和 Spring boot版本号是否匹配。
3、注册到注册中心
将gulimall-third-party注册到注册中心
在nacos后台创建第三方微服务的命名空间,名称:third-party
在Nacos注册中心创建配置文件 oss.yml
alibaba:
cloud:
access-key: LTAI4Fq***************2aTCJ
secret-key: Ueb6c******************ZtPXUqi
oss:
endpoint: oss-cn-heyuan.aliyuncs.com
新建文件:gulimall-third-party/src/main/resources/bootstrap.properties
spring.application.name=gulimall-third-party
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.namespace=510ff354-cad2-4807-8040-ad7c03c02e62
spring.cloud.nacos.config.ext-config[0].data-id=oss.yml
spring.cloud.nacos.config.ext-config[0].group=DEFAULT_GROUP
# 动态刷新,改了配置之后会自动刷新
spring.cloud.nacos.config.ext-config[0].refresh=true
创建 gulimall-third-party/src/main/resources/application.yml
spring:
# 配置nacos注册中心
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
application:
name: gulimall-third-party
server:
port: 30000
修改gulimall-third-party/pom.xml
文件,排除掉 mybatis-plus
的引入依赖
<dependency>
<groupId>com.atguigu.gulimall</groupId>
<artifactId>gulimall-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
<exclusions>
<!-- 由于third-party暂时不需要连接数据库服务,所以,在导入common微服务的时候,直接排除掉mybatis-plus,否则会报错[20200819] -->
<exclusion>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
启动文件添加注册发现注解:gulimall/thirdparty/GulimallThirdPartyApplication.java
@EnableDiscoveryClient // 添加注册发现功能
@SpringBootApplication
public class GulimallThirdPartyApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallThirdPartyApplication.class, args);
}
}
然后启动 gulimall-third-party 微服务,在Nacos看是否已经注册成功。
我们可以看到,已经成功启动并且注入到Nacos注册中心了 ^_^
4、单元测试
上边已经添加到注册中心了,我们在单元测试调用OSS接口上传到阿里云:thirdparty/GulimallThirdPartyApplicationTests.java
package com.atguigu.gulimall.thirdparty;
import com.aliyun.oss.OSS;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.FileInputStream;
@RunWith(SpringRunner.class)
@SpringBootTest
public class GulimallThirdPartyApplicationTests {
@Autowired
public OSS ossClient;
@Test
public void contextLoads() {
}
@Test
public void ossTest(){
try {
String bucket_name = "gulimall-corwien";
FileInputStream inputStream = new FileInputStream("/Users/kaiyiwang/Desktop/toutiao/unnamed.jpg");
ossClient.putObject(bucket_name, "oss-upload-test.jpg", inputStream);
}
catch (Exception e) {
e.printStackTrace();
System.out.println("upload fail: " + e.getMessage());
}
System.out.println("upload success ");
}
}
修改Nacos 配置中心下的 gulimall-third-party
的 oss.yml
配置文件,去掉 endpoint 连接的 bucket_name,这个name 需要我们动态传递:
alibaba:
cloud:
access-key: LTAI4Fq***********y2aTCJ
secret-key: Ueb6cWiG********5ZtPXUqi
oss:
endpoint: oss-cn-heyuan.aliyuncs.com
# 代码调用上传时不需要bucket_name 前缀,代码里边会动态加入
# gulimall-corwien.oss-cn-heyuan.aliyuncs.com
然后重新发布配置,再运行ossTest()测试方法,控制台运行成功,我们登录到阿里云OSS后台,看是否有上传,可以看到,川建国同志已经上传成功了^_^
二、OSS获取服务端签名
服务端签名直传请参考官方文档:最佳实践-服务端签名后直传
为了便于调试,把OSS配置写在本地 gulimall-third-party/src/main/resources/application.yml
文件:
spring:
# 配置nacos注册中心
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
application:
name: gulimall-third-party
server:
port: 30000
alibaba:
cloud:
access-key: LTAI4FqLgxxxxxxxxxxxAApy2aTCJ
secret-key: Ueb6cWiGxxxxxxxxx68f5ZtPXUqi
oss:
endpoint: oss-cn-heyuan.aliyuncs.com
bucket: gulimall-corwien
新建文件:gulimall-third-party/src/main/java/com/atguigu/gulimall/thirdparty/controller/OssController.java
package com.atguigu.gulimall.thirdparty.controller;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author: kaiyi
* @create: 2020-08-20 02:02
*/
@RestController
@RequestMapping("thirdparty")
public class OssController {
@Autowired
OSS ossClient;
@Value("${alibaba.cloud.oss.endpoint}")
private String endpoint;
@Value("${alibaba.cloud.oss.bucket}")
private String bucket;
@Value("${alibaba.cloud.access-key}")
private String accessId;
@RequestMapping("/oss/policy")
public Map<String, String> policy(){
// host的格式为 bucketname.endpoint
String host = "https://" + bucket + "." + endpoint;
// callbackUrl为 上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
String callbackUrl = "http://88.88.88.88:8888";
// 用户上传文件时指定的前缀,也就是目录
String format = new SimpleDateFormat("YYYY-MM-dd").format(new Date());
String dir = format;
// 创建OSSClient实例,引入自动依赖注入,这里不需要再new了
// OSS ossClient = new OSSClientBuilder().build(endpoint, accessId, accessKey);
Map<String, String> respMap = null;
try {
long expireTime = 30;
long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
Date expiration = new Date(expireEndTime);
// PostObject请求最大可支持的文件大小为5 GB,即CONTENT_LENGTH_RANGE为5*1024*1024*1024。
PolicyConditions policyConds = new PolicyConditions();
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
byte[] binaryData = postPolicy.getBytes("utf-8");
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = ossClient.calculatePostSignature(postPolicy);
respMap = new LinkedHashMap<String, String>();
respMap.put("accessid", accessId);
respMap.put("policy", encodedPolicy);
respMap.put("signature", postSignature);
respMap.put("dir", dir);
respMap.put("host", host);
respMap.put("expire", String.valueOf(expireEndTime / 1000));
// respMap.put("expire", formatISO8601Date(expiration));
/*
JSONObject jasonCallback = new JSONObject();
jasonCallback.put("callbackUrl", callbackUrl);
jasonCallback.put("callbackBody",
"filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");
jasonCallback.put("callbackBodyType", "application/x-www-form-urlencoded");
String base64CallbackBody = BinaryUtil.toBase64String(jasonCallback.toString().getBytes());
respMap.put("callback", base64CallbackBody);
*/
} catch (Exception e) {
// Assert.fail(e.getMessage());
System.out.println(e.getMessage());
} finally {
ossClient.shutdown();
}
return respMap;
}
}
然后启动服务,访问 http://localhost:30000/thirdparty/oss/policy
,获取OSS的签名校验:
{
"accessid": "LTAI4FqLgSsw8YKAApy2aTCJ",
"policy": "eyJleHBpcmF0aW9uIjoiMjAyMC0wOC0yMFQwMzoyMDozMy4wMjRaIiwiY29uZGl0aW9ucyI6W1siY29udGVudC1sZW5ndGgtcmFuZ2UiLDAsMTA0ODU3NjAwMF0sWyJzdGFydHMtd2l0aCIsIiRrZXkiLCIyMDIwLTA4LTIwIl1dfQ==",
"signature": "J/m1D3d0pa9dsbQzg1VEDUL9AO8=",
"dir": "2020-08-20",
"host": "https://gulimall-corwien.oss-cn-heyuan.aliyuncs.com",
"expire": "1597893633"
}
我们可以看到,OSS返回这样的签名校验结果,有上传的host,上传的目录,过期时间等。
三、将第三方微服务(OSS)请求加入网关
将请求第三方微服务加入到 gateway 网关服务,以后可以直接通过网关请求。
修改 gulimall-gateway/src/main/resources/application.yml
spring:
cloud:
gateway:
routes:
- id: product_route
uri: lb://gulimall-product
predicates:
- Path=/api/product/**
filters:
- RewritePath=/api/(?<segment>.*),/$\{segment}
# 新增加第三方网关路由
- id: third_party_route
uri: lb://gulimall-third-party
predicates:
- Path=/api/thirdparty/**
filters:
- RewritePath=/api/(?<segment>.*),/$\{segment}
- id: admin_route
uri: lb://renren-fast
predicates:
- Path=/api/**
filters:
- RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}
## 前端项定义规则,都带 /api 前缀, lb 表示负载均衡到哪个注册器
## http://localhost:8888/api/captcha.jpg 需要通过注册中心网关8888端口转发到renren-fast 8080端口服务
## http://localhost:8080/renren-fast/captcha.jpg:
## filters 路径重写 /api/ -> /renren-fast/
重启网关服务,然后通过路由访问测试:
- 直接访问第三方OSS服务:http://localhost:30000/thirdparty/oss/policy
- 通过路由访问OSS服务:http://localhost:8888/api/thirdparty/oss/policy
通过路由访问一样也可以拿到签名校验结果。
为者常成,行者常至
自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)