文章目录
- 前言
- 一、文件服务
-
- [1.1 为啥要封装文件服务?](#1.1 为啥要封装文件服务?)
- [1.2 对象存储](#1.2 对象存储)
- [1.3 开通 OSS](#1.3 开通 OSS)
-
- [1.3.1 登录/注册阿里云](#1.3.1 登录/注册阿里云)
- [1.3.2 创建 AccessKey](#1.3.2 创建 AccessKey)
- [1.3.3 开通oss服务](#1.3.3 开通oss服务)
- [1.3.4 创建bucket](#1.3.4 创建bucket)
- [1.3.5 ossbrowser安装](#1.3.5 ossbrowser安装)
- [1.3.6 ossbrowser 使用](#1.3.6 ossbrowser 使用)
- [1.4 创建文件服务](#1.4 创建文件服务)
-
- [1.4.1 创建文件服务](#1.4.1 创建文件服务)
- [1.4.2 在 service 中添加依赖](#1.4.2 在 service 中添加依赖)
- [1.4.3 创建 config 包创建配置文件](#1.4.3 创建 config 包创建配置文件)
- [1.4.4 配置 nacos](#1.4.4 配置 nacos)
- [1.4.5 接口设计](#1.4.5 接口设计)
- [1.4.6 代码实现](#1.4.6 代码实现)
-
- [1.4.6.1 先在 domain 包下定义dto、vo](#1.4.6.1 先在 domain 包下定义dto、vo)
- [1.4.6.2 在 controller 包里创建 FileController 类](#1.4.6.2 在 controller 包里创建 FileController 类)
- [1.4.6.3 在 service 包里创建 IFileService 接口及其实现类](#1.4.6.3 在 service 包里创建 IFileService 接口及其实现类)
- [1.4.6.4 创建 constants 包将 OSS 的常量放进去](#1.4.6.4 创建 constants 包将 OSS 的常量放进去)
- [1.4.7 加入 bootstrap.yml 配置文件](#1.4.7 加入 bootstrap.yml 配置文件)
- 代码都是根据官网文档提供的模板的修改,简单调用
- [二、apifox 校验](#二、apifox 校验)
-
- [2.1 文件直传接口](#2.1 文件直传接口)
- [2.2 获取直传签名接口和前端文件直传接口](#2.2 获取直传签名接口和前端文件直传接口)
- 三、鸟瞰图
- END
鸡汤:
● 读一页书、跑五百米、写三行计划......世界从不辜负认真耕耘的人------你流的每一滴汗,时间都悄悄记在了未来的答卷上。
●世界从不辜负那些把"再试一次"刻进骨子里的人。
前言
前面我们将一些通用的工具类和一些通用的中间件封装了一下,接下来就要开始基础的服务开发。
一、文件服务
1.1 为啥要封装文件服务?
当然是常用了,现在对于文件的上传、下载功能是软件系统中最常见的功能。例如:电商系统中需要上传商品的图片、广告视频,办公系统中上传附件,社交类系统中上传用户头像等等。
那我们说那搞一个文件模块不就可以了为啥要搞出一个文件服务呢?

这样存在什么问题呢?
● 重复配置开发:每个业务微服务都需要对接该服务商,必然会出现重复的配置或开发工作。
● 可维护性差: 当需要切换后台存储/或者服务商时,所有涉及到的微服务都需要修改、测试、上线。
● 扩展性差:文件存储和传输可能消耗大量带宽、存储和计算资源。独立部署后,可根据需求单独横向扩展文件服务。
● 资源隔离:文件存储和流量消耗可单独监控计费,便于成本分摊和优化。
基于以上原因,微服务体系下的应用系统一般都有一个文件服务,用于统一管理文件上传下载等功能。
1.2 对象存储
对象存储(Object Storage) 是一种用于存储和管理非结构化数据的存储架构。与传统文件系统(如块存储或文件存储)不同,对象存储将数据存储为独立的"对象",每个对象包含数据本身、元数据以及唯一的全局标识符(如对象 ID),结合元数据和唯一的标识符可以进行数据检索。对象存储通常用于存储大量非结构化数据,如图片、视频、日志文件、备份数据等。对象存储将所有对象一起存储在一个"数据湖"(也称之为"数据池")中。因此,对象存储可以非常快速地存储大量数据,就像打包旅行时,将衣服塞进袋子里比仔细地将衣服叠好并分类放李箱中要快。
对象存储是一种高效、可扩展的存储方式,适合处理大规模非结构化数据,广泛应用于云存储、备份、大数据分析等领域。
1.3 开通 OSS
1.3.1 登录/注册阿里云
如果没有阿里云账号需要注册
1.3.2 创建 AccessKey
步骤一:点击 AccessKey

步骤二:点击使用 RAM 用户 AccessKey

步骤三:点击执行配置

步骤四:完成校验(没截图)
步骤五:将 AccessKey ID 和 AccessKey Secret 下载下来

1.3.3 开通oss服务
● 首次开通 oss 是可以免费试用

之后就可以跟着教程走了,遇到不明白的可以问客服

客服会解答你的问题的。
1.3.4 创建bucket


● 创建目录便于我们对上传的文件进行管理

1.3.5 ossbrowser安装
为了方便操作,在使用 OSS 时可以下载 ossbrowser 操作 oss。在 oss 官方文档中找到 oss 常用工具,选择ossbrowser2.0 进行安装。

点击后就直接开始下载,根据官方教程指引完成安装即可。
1.3.6 ossbrowser 使用
1.登录 ossbrowser
我们选择账号登录的方式完成登录。

- ossbrowser 基本使用
登录成功之后可根据需要操作 ossbrowser ,官网中也给出来相应的参考文档。

1.4 创建文件服务
1.4.1 创建文件服务
创建方式和(mstemplate)模板服务相同,就不再多说了

1.4.2 在 service 中添加依赖
pom.xml:
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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.my</groupId>
<artifactId>file</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.my</groupId>
<artifactId>file-service</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>com.my</groupId>
<artifactId>common-core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 阿里云OSS-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
</dependency>
<dependency>
<groupId>com.my</groupId>
<artifactId>common-security</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.45.0</version>
<configuration>
<!--指定远程服务器的Docker服务访问地址-->
<dockerHost>${dockerhost.addr}</dockerHost>
<certPath>${project.basedir}/../../deploy/test/app/config/cert</certPath>
<!--指定私有仓库的访问路径-->
<pushRegistry>${dockerregistry.url}</pushRegistry>
<!--指定库的用户名与密码-->
<authConfig>
<username>${dockerregistry.username}</username>
<password>${dockerregistry.password}</password>
</authConfig>
<containerNamePattern>${dockerregistry.namespace}-%n-%i</containerNamePattern>
<!-- <stopNamePattern>${dockerregistry.namespace}-%n-*</stopNamePattern>-->
<images>
<image>
<!--指定私有仓库访问地址/镜像名称-->
<name>${dockerregistry.namespace}/${project.build.finalName}:${project.version}</name>
<build>
<!--指定Dockerfile的路径-->
<dockerFileDir>${project.basedir}</dockerFileDir>
</build>
<run>
<env>
<NACOS_ADDR>${nacos.addr}</NACOS_ADDR>
<RUN_ENV>${runenv}</RUN_ENV>
<JAVA_OPTS>${java.opts.default}</JAVA_OPTS>
</env>
</run>
</image>
</images>
</configuration>
<executions>
<execution>
<id>build</id>
<phase>deploy</phase>
<goals>
<goal>stop</goal>
<goal>build</goal>
<goal>start</goal>
<!--推送到远端仓库 -->
<goal>push</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
1.4.3 创建 config 包创建配置文件

OSSAutoConfiguration:
java
package com.my.fileservice.config;
import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyuncs.exceptions.ClientException;
import jakarta.annotation.PreDestroy;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 阿里云OSS auto config
*
*/
@Configuration
@ConditionalOnProperty(value = "storage.type", havingValue = "oss")
// 当括号中条件成立时,微服务启动的时候就会加载当前类。
public class OSSAutoConfiguration {
/**
* oss客户端
*/
public OSSClient ossClient;
/**
* 初始化客户端
* @param prop oss配置
* @return ossclient
* @throws ClientException 客户端异常
*/
@Bean
public OSSClient ossClient(OSSProperties prop) throws ClientException {
// ref: https://help.aliyun.com/document_detail/32011.html?spm=a2c4g.32010.0.0.33386a03cVRCNW
// EnvironmentVariableCredentialsProvider credentialsProvider =
// CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
DefaultCredentialProvider credentialsProvider = CredentialsProviderFactory.newDefaultCredentialProvider(
prop.getAccessKeyId(), prop.getAccessKeySecret());
// 创建ClientBuilderConfiguration
ClientBuilderConfiguration conf = new ClientBuilderConfiguration();
conf.setSignatureVersion(SignVersion.V4);
// 使用内网endpoint进行上传 prop.getIntEndpoint()
if (prop.getInternal()){
ossClient = (OSSClient) OSSClientBuilder.create()
.endpoint(prop.getIntEndpoint())
.region(prop.getRegion())
.credentialsProvider(credentialsProvider)
.clientConfiguration(conf)
.build();
} else {
ossClient = (OSSClient) OSSClientBuilder.create()
.endpoint(prop.getEndpoint())
.region(prop.getRegion())
.credentialsProvider(credentialsProvider)
.clientConfiguration(conf)
.build();
}
return ossClient;
}
/**
* 关闭客户端
*/
@PreDestroy
public void closeOSSClient() {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
OSSProperties:
java
package com.my.fileservice.config;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
/**
* 阿里云OSS 配置信息,从nacos读取
*/
@Slf4j
@Data
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "oss")
@ConditionalOnProperty(value = "storage.type", havingValue = "oss")
public class OSSProperties {
/**
* oss是否内网上传
*/
private Boolean internal;
/**
* oss的endpoint
*/
private String endpoint;
/**
* oss的endpoint的内部地址
*/
private String intEndpoint;
/**
* 地域的代码
*/
private String region;
/**
* ak
*/
private String accessKeyId;
/**
* sk
*/
private String accessKeySecret;
/**
*存储桶
*/
private String bucketName;
/**
* 路径前缀,加在 endPoint 之后
*/
private String pathPrefix;
private Integer expre;
private Integer minLen;
private Integer maxLen;
/**
* 获取访问URL
*
* @return url信息
*/
public String getBaseUrl() {
return "https://" + bucketName + "." + endpoint + "/";
}
/**
* 获取内部访问URL
*
* @return 内部访问URL
*/
public String getInternalBaseUrl() {
return "http://" + bucketName + "." + intEndpoint + "/";
}
}
注:
这里的 OSSProperties 是配置在 nacos 里的配置,而 OSSAutoConfiguration 是在阿里云文档上查的
1.4.4 配置 nacos

yaml
storage:
type: oss
oss:
internal: ${FILE_UPLOAD_INTERNAL:false}
endpoint: oss-cn-chengdu.aliyuncs.com
intEndpoint: oss-cn-chengdu-internal.aliyuncs.com
region: cn-beijing
accessKeyId: LTAI5tHAC8nnazhjD6AJ3Ax5
accessKeySecret: GspXyNp72gVOM493jqYt3noqeKAn3b
bucketName: frameworkjava-demo
presignedUrlExpireInMinutes: 60
pathPrefix: folder/
expre: 600
minLen: 0
maxLen: 1073741824
spring:
servlet:
multipart:
max-file-size: 50MB
max-request-size: 50MB
1.4.5 接口设计
1 . 简单上传
直接传一个文件,返回包含VO

2 . 前端获取直传签名
前端来一个 Get 请求,返回直传签名信息

1.4.6 代码实现
创建 controller 包、domain 包、service 包以及启动类FileServiceApplication

1.4.6.1 先在 domain 包下定义dto、vo

dto:
FileDTO:
java
package com.my.fileservice.domain.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class FileDTO {
private String url;
//路径信息 /目录/文件名.后缀名
private String key;
private String name;
}
SignDTO:
java
package com.my.fileservice.domain.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class SignDTO {
/**
* 签名
*/
private String signature;
private String host;
private String pathPrefix;
private String xOSSCredential;
private String xOSSDate;
private String policy;
}
vo:
FileVO:
java
package com.my.fileservice.domain.vo;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class FileVO {
private String url;
//路径信息 /目录/文件名.后缀名
private String key;
private String name;
}
SignVO:
java
package com.my.fileservice.domain.vo;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class SignVO {
/**
* 签名
*/
private String signature;
private String host;
private String pathPrefix;
private String xOSSCredential;
private String xOSSDate;
private String policy;
}
1.4.6.2 在 controller 包里创建 FileController 类

FileController:
java
package com.my.fileservice.controller;
import com.my.commoncore.utils.BeanCopyUtil;
import com.my.commondomain.domain.vo.R;
import com.my.fileservice.domain.dto.FileDTO;
import com.my.fileservice.domain.dto.SignDTO;
import com.my.fileservice.domain.vo.FileVO;
import com.my.fileservice.domain.vo.SignVO;
import com.my.fileservice.service.IFileService;
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.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@RestController
@Slf4j
public class FileController {
@Autowired
private IFileService fileService;
@PostMapping("/upload")
public R<FileVO> upload(MultipartFile file) {
FileDTO fileDTO = fileService.upload(file);
FileVO fileVO = new FileVO();
BeanCopyUtil.copyProperties(fileDTO, fileVO);
return R.success(fileVO);
}
@GetMapping("/sign")
public R<SignVO> getSign() {
SignDTO signDTO = fileService.getSign();
SignVO signVO = new SignVO();
BeanCopyUtil.copyProperties(signDTO, signVO);
return R.success(signVO);
}
}
1.4.6.3 在 service 包里创建 IFileService 接口及其实现类

IFileService:
java
package com.my.fileservice.service;
import com.my.fileservice.domain.dto.FileDTO;
import com.my.fileservice.domain.dto.SignDTO;
import org.springframework.web.multipart.MultipartFile;
public interface IFileService {
FileDTO upload(MultipartFile file);
SignDTO getSign();
}
OSSFileService:
java
package com.my.fileservice.service.Impl;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.my.commoncore.utils.JsonUtil;
import com.my.commondomain.domain.vo.ResultCode;
import com.my.commondomain.exception.ServiceException;
import com.my.fileservice.config.OSSProperties;
import com.my.fileservice.constants.OSSCustomConstants;
import com.my.fileservice.domain.dto.FileDTO;
import com.my.fileservice.domain.dto.SignDTO;
import com.my.fileservice.service.IFileService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.InputStream;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
@Service
@Slf4j
@ConditionalOnProperty(value = "storage.type", havingValue = "oss")
public class OSSFileService implements IFileService {
@Autowired
private OSSClient ossClient;
@Autowired
private OSSProperties ossProperties;
DateTimeFormatter timeoutFormatter = DateTimeFormatter.ofPattern(OSSCustomConstants.SIGN_EXPIRE_TIME_FORMAT)
.withZone(ZoneOffset.UTC);
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(OSSCustomConstants.SIGN_DATE_FORMAT)
.withZone(ZoneOffset.UTC);
DateTimeFormatter requestDateFormatter = DateTimeFormatter.ofPattern(OSSCustomConstants.SIGN_REQUEST_TIME_FORMAT)
.withZone(ZoneOffset.UTC);
@Override
public FileDTO upload(MultipartFile file) {
try {
InputStream inputStream = file.getInputStream();
//获取原始的文件名
String originalFilename = file.getOriginalFilename();
String extName = originalFilename.substring(originalFilename.lastIndexOf(".")+1);
//在oss中存储名字就是UUID + 文件的后缀名
String objectName = ossProperties.getPathPrefix() + UUID.randomUUID()+"."+extName;
ObjectMetadata objectMetadata = new ObjectMetadata();
// set public read
objectMetadata.setObjectAcl(CannedAccessControlList.PublicRead);
// 创建PutObjectRequest对象。
PutObjectRequest putObjectRequest = new PutObjectRequest(ossProperties.getBucketName(), objectName, inputStream, objectMetadata);
// 创建PutObject请求。
PutObjectResult putObjectResult = ossClient.putObject(putObjectRequest);
if (putObjectResult == null || StringUtils.isBlank(putObjectResult.getRequestId())) {
log.error("上传oss异常putObjectResult未正常返回: {}", putObjectRequest);
throw new ServiceException(ResultCode.OSS_UPLOAD_FAILED);
}
FileDTO sysFileDTO = new FileDTO();
sysFileDTO.setUrl(ossProperties.getBaseUrl() + objectName);
sysFileDTO.setKey(objectName);
sysFileDTO.setName(new File(objectName).getName());
return sysFileDTO;
} catch (Exception e) {
log.error("上传oss异常", e);
throw new ServiceException(ResultCode.OSS_UPLOAD_FAILED);
}
}
@Override
public SignDTO getSign() {
try {
String accesskeyid = ossProperties.getAccessKeyId();
String accesskeysecret = ossProperties.getAccessKeySecret();
String region = ossProperties.getRegion();
// 获取当前时间
Instant now = Instant.now();
SignDTO signDTO = new SignDTO();
signDTO.setHost(ossProperties.getBaseUrl());
signDTO.setPathPrefix(ossProperties.getPathPrefix());
Map<String, Object> policy = new HashMap<>();
policy.put("expiration", timeoutFormatter.format(now.plusSeconds(ossProperties.getExpre())));
List<Object> conditions = new ArrayList<>();
Map<String, String> bucketCondition = new HashMap<>();
bucketCondition.put("bucket", ossProperties.getBucketName());
conditions.add(bucketCondition);
Map<String, String> signatureVersionCondition = new HashMap<>();
signatureVersionCondition.put("x-oss-signature-version", "OSS4-HMAC-SHA256");
conditions.add(signatureVersionCondition);
Map<String, String> credentialCondition = new HashMap<>();
String xOSSCredential = accesskeyid + "/" + dateFormatter.format(now) + "/" + region + "/oss/aliyun_v4_request";
signDTO.setXOSSCredential(xOSSCredential);
credentialCondition.put("x-oss-credential", xOSSCredential); // 替换为实际的 access key id
conditions.add(credentialCondition);
Map<String, String> dateCondition = new HashMap<>();
String xOSSDate = requestDateFormatter.format(now);
signDTO.setXOSSDate(xOSSDate);
dateCondition.put("x-oss-date", xOSSDate);
conditions.add(dateCondition);
conditions.add(Arrays.asList("content-length-range", ossProperties.getMinLen(), ossProperties.getMaxLen()));
conditions.add(Arrays.asList("eq", "$success_action_status", "200"));
// conditions.add(Arrays.asList("starts-with", "$key", "user/eric/"));
// conditions.add(Arrays.asList("in", "$content-type", Arrays.asList("image/jpg", "image/png")));
// conditions.add(Arrays.asList("not-in", "$cache-control", Arrays.asList("no-cache")));
policy.put("conditions", conditions);
String jsonPolicy = JsonUtil.toJson(policy);
// 步骤2:构造待签名字符串(StringToSign)。
String policyBase64 = new String(Base64.encodeBase64(jsonPolicy.getBytes()));
signDTO.setPolicy(policyBase64);
// 步骤3:计算SigningKey。
byte[] dateKey = hmacsha256(("aliyun_v4" + accesskeysecret).getBytes(), dateFormatter.format(now));
byte[] dateRegionKey = hmacsha256(dateKey, region);
byte[] dateRegionServiceKey = hmacsha256(dateRegionKey, "oss");
byte[] signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request");
// 步骤4:计算Signature。
byte[] result = hmacsha256(signingKey, policyBase64);
String signature = BinaryUtil.toHex(result);
signDTO.setSignature(signature);
return signDTO;
} catch (Exception e) {
log.error("生成直传签名失败",e);
throw new ServiceException(ResultCode.PRE_SIGN_URL_FAILED);
}
}
public static byte[] hmacsha256(byte[] key, String data) {
try {
// 初始化HMAC密钥规格,指定算法为HMAC-SHA256并使用提供的密钥。
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256");
// 获取Mac实例,并通过getInstance方法指定使用HMAC-SHA256算法。
Mac mac = Mac.getInstance("HmacSHA256");
// 使用密钥初始化Mac对象。
mac.init(secretKeySpec);
// 执行HMAC计算,通过doFinal方法接收需要计算的数据并返回计算结果的数组。
byte[] hmacBytes = mac.doFinal(data.getBytes());
return hmacBytes;
} catch (Exception e) {
log.error("生成直传签名失败", e);
throw new ServiceException(ResultCode.PRE_SIGN_URL_FAILED);
}
}
}
1.4.6.4 创建 constants 包将 OSS 的常量放进去

OSSCustomConstants:
java
package com.my.fileservice.constants;
public class OSSCustomConstants {
/**
* 签名过期时间格式
*/
public final static String SIGN_EXPIRE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
/**
* 请求时间格式
*/
public final static String SIGN_REQUEST_TIME_FORMAT = "yyyyMMdd'T'HHmmss'Z'";
/**
* 请求的日期格式
*/
public final static String SIGN_DATE_FORMAT = "yyyyMMdd";
}
1.4.7 加入 bootstrap.yml 配置文件

bootstrap.yml:
java
server:
port: 18081
spring:
application:
name: file
profiles:
active: ${RUN_ENV}
cloud:
nacos:
discovery:
username: nacos
password: bite@123
namespace: frameworkjava-${RUN_ENV}
server-addr: ${NACOS_ADDR}
config:
username: nacos
password: bite@123
namespace: frameworkjava-${RUN_ENV}
server-addr: ${NACOS_ADDR}
file-extension: yaml
logging:
pattern:
console: '[%thread] %-5level %logger{36} - %msg%n'
代码都是根据官网文档提供的模板的修改,简单调用
二、apifox 校验
启动网关服务和文件服务,用 apifox 进行校验
2.1 文件直传接口
未发送前:


发送后:


2.2 获取直传签名接口和前端文件直传接口
未发送前:



发送后:

这里是提取了环境变量(之前手动CV一直错)


啥也没返回就是成功了

三、鸟瞰图
因为文件服务是为前端服务的,服务之间不会远程调用所以,file-api 没有东西

END
文件服务封装完毕!
