Java项目:Java脚手架项目的文件服务(八)

文章目录

  • 前言
  • 一、文件服务
    • [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

我们选择账号登录的方式完成登录。

  1. 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

文件服务封装完毕!

相关推荐
毅炼1 小时前
Java 集合常见问题总结(3)
java·开发语言·后端
崎岖Qiu2 小时前
【计算机网络 | 第十一篇】图解交换机的自学习功能
网络·学习·计算机网络
沐知全栈开发2 小时前
ionic 对话框:深度解析与最佳实践
开发语言
科技林总2 小时前
【系统分析师】8.0 项目管理
学习
浅念-2 小时前
C++ string类
开发语言·c++·经验分享·笔记·学习
百锦再2 小时前
Java多线程编程全面解析:从原理到实战
java·开发语言·python·spring·kafka·tomcat·maven
Cosmoshhhyyy2 小时前
《Effective Java》解读第38条:用接口模拟可扩展的枚举
java·开发语言
Purple Coder3 小时前
基于神经网络的家教系统
学习
wangbing11253 小时前
平台介绍-主数据系统-同步消息设计
java