• SpringBoot对接阿里云OSS上传文件以及回调(有坑)


    前言

    今天在对接阿里云OSS对象存储, 把这过程记录下来

    链接

    阿里云的内容很多,文档是真的难找又难懂
    本文主要是用的PostObject API 加上 Callback参数
    PostObject -> https://help.aliyun.com/document_detail/31988.html?spm=a2c4g.31989.0.0
    Callback -> https://help.aliyun.com/document_detail/31989.html?spm=a2c4g.31988.0.0

    对接过程

    1. 前端向后端发送请求获取签名
    2. 后端与OSS服务器交互,返回前端签名
    3. 前端拿到签名,直接上传到OSS服务器
    4. 上传成功,OSS回调应用服务器,应用服务器给前端返回上传的信息

    相比应用服务器直接上传到OSS,大大减少了带宽和应用服务器的压力,缺点就是相对麻烦一点

    1. pom.xml

    我们用的是SpringBoot,我们导入aliyun-oss-spring-boot-starter包,因为我这个是 SpringCloud项目父项目管理了SpringCloud 和 SpringCloud Alibaba 、SpringBoot版本的,所以一开始我是没有写版本号的,结果一直爆红,导不进来,然后仔细一看,原来这是aliyun-oss-spring-boot-starter,注意这个boot-starter,我还以为是cloud系列的,然而哈哈,然后导入aliyun-spring-boot-dependencies,我在仓库里面去找版本号,然而它也只有这一个1.0.0版本

    <dependencies>
    	<dependency>
    		<groupId>org.springframework.bootgroupId>
    		<artifactId>spring-boot-starter-webartifactId>
    	dependency>
    
    	
    	<dependency>
    		<groupId>com.alibaba.cloudgroupId>
    		<artifactId>aliyun-oss-spring-boot-starterartifactId>
    	dependency>
    dependencies>
    <dependencyManagement>
    	<dependency>
    		<groupId>com.alibaba.cloudgroupId>
    		<artifactId>aliyun-spring-boot-dependenciesartifactId>
    		<version>1.0.0version>
    		<type>pomtype>
    		<scope>importscope>
          dependency>
    dependencyManagement>
    

    2. application.yml

    alibaba:
      cloud:
        # 阿里控制台OSS子账户的信息 
        access-key: LTAI5tDZ51bANyHxYpwZzvpi
        secret-key: zPxHPuaZMTPzTPy0WF88vI99HHLOzO
        oss:
          # 深圳endpoint
          endpoint: oss-cn-shenzhen.aliyuncs.com
          # 认证过期 单位秒
          expireTime: 1200
          # 储存空间名字
          bucket: yues-oss
    

    3. 核心代码

    package com.yues.gulimall.thirdparty.controller;
    
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.common.utils.BinaryUtil;
    import com.aliyun.oss.model.Callback;
    import com.aliyun.oss.model.MatchMode;
    import com.aliyun.oss.model.PolicyConditions;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.servlet.http.HttpServletRequest;
    import java.nio.charset.StandardCharsets;
    import java.text.SimpleDateFormat;
    import java.util.*;
    
    @RestController
    public class OssController {
        @Autowired
        private OSS ossClient;
        @Value("${alibaba.cloud.oss.endpoint}")
        private String endpoint;
        @Value("${alibaba.cloud.access-key}")
        private String accessId;
        @Value("${alibaba.cloud.oss.expireTime}")
        private long expireTime;
        @Value("${alibaba.cloud.oss.bucket}")
        private String bucket;
    
        /**
         * 回调
         * @param request request
         * @return Map
         */
        @RequestMapping("/ossCallback")
        public Map ossCallback(HttpServletRequest request) {
            HashMap result = new HashMap<>();
            String filename = request.getParameter("filename");
            Map parameterMap = request.getParameterMap();
            parameterMap.forEach((item,value) -> {
                System.out.println(item);
                System.out.println(Arrays.toString(value));
            });
            filename = "https://".concat(bucket).concat(".").concat(endpoint).concat("/").concat(filename);
            result.put("url", filename);
            result.put("size", request.getParameter("size"));
            result.put("mimeType", request.getParameter("mimeType"));
            return result;
        }
    
    
        /**
         * 签名
         * @return Map
         */
        @PostMapping("/getOssSecret")
        public Map getOssSign() {
            // 填写Host地址,格式为https://bucketname.endpoint。
            String host = "https://" + bucket + "."+ endpoint;
            // OSS会在文件上传完成后,把文件上传信息发送给应用服务器。需要外网ip
            String callbackUrl = "https://23d4-1-196-175-217.ngrok-free.app/ossCallback";
            // 设置上传到OSS文件的前缀,可置空此项。置空后,文件将上传至Bucket的根目录下。
            // 将日期作为上传的文件夹目录
            String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
            String dir = format+"/";
            Map respMap = null;
            try {
                long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
                // 过期时间
                Date expiration = new Date(expireEndTime);
                // 上传回调参数。
                Callback callback = new Callback();
                callback.setCallbackUrl(callbackUrl);
                // 设置回调请求消息头中Host的值,即您的服务器配置Host的值。需要外网ip
                 callback.setCallbackHost("23d4-1-196-175-217.ngrok-free.app");
                // 设置发起回调时请求body的值。
                callback.setCallbackBody("filename=${object}&size=${size}&mimeType=${mimeType}");
                // 设置发起回调请求的Content-Type。
                callback.setCalbackBodyType(Callback.CalbackBodyType.JSON);
                // 设置发起回调请求的自定义参数,由Key和Value组成,Key必须以x:开始。
                callback.addCallbackVar("x:var1", "value1");
                callback.addCallbackVar("x:var2", "value2");
                // 序列化callback
                String callbackString = BinaryUtil.toBase64String(new ObjectMapper().writeValueAsString(callback).getBytes(StandardCharsets.UTF_8));
    
                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(StandardCharsets.UTF_8);
                // 转为Base64
                String encodedPolicy = BinaryUtil.toBase64String(binaryData);
                String postSignature = ossClient.calculatePostSignature(postPolicy);
    
                respMap = new LinkedHashMap<>();
                respMap.put("OSSAccessKeyId", accessId);
                respMap.put("policy", encodedPolicy);
                respMap.put("Signature", postSignature);
                respMap.put("dir", dir);
                respMap.put("host", host);
                respMap.put("callback", callbackString);
                // 过期时间的时间戳
                respMap.put("expire", String.valueOf(expireEndTime / 1000));
            } catch (Exception e) {
                throw new RuntimeException("aliyun上传文件获取认证信息失败:" + e.getMessage());
            }
            return  respMap;
        }
    }
    

    4. postman 调用获取签名返回

    {
        "OSSAccessKeyId": "LTAI5tDZ51bANyHxYpwZzvpi",
        "policy": "eyJleHBpcmF0aW9uIjoiMjAyMy0wNy0wM1QxNzoxNzo1MS44ODBaIiwiY29uZGl0aW9ucyI6W1siY29udGVudC1sZW5ndGgtcmFuZ2UiLDAsMTA0ODU3NjAwMF0sWyJzdGFydHMtd2l0aCIsIiRrZXkiLCIyMDIzLTA3LTA0LyJdXX0=",
        "Signature": "yK5oF8Cj0ncR0ceuIMtXdMW1yco=",
        "dir": "2023-07-04/",
        "host": "https://yues-oss.oss-cn-shenzhen.aliyuncs.com",
        "callback": "eyJjYWxsYmFja1VybCI6Imh0dHBzOi8vMjNkNC0xLTE5Ni0xNzUtMjE3Lm5ncm9rLWZyZWUuYXBwL29zc0NhbGxiYWNrIiwiY2FsbGJhY2tIb3N0IjoiMjNkNC0xLTE5Ni0xNzUtMjE3Lm5ncm9rLWZyZWUuYXBwIiwiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JHtvYmplY3R9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSIsImNhbGJhY2tCb2R5VHlwZSI6IkpTT04iLCJjYWxsYmFja1ZhciI6eyJ4OnZhcjIiOiJ2YWx1ZTIiLCJ4OnZhcjEiOiJ2YWx1ZTEifX0=",
        "expire": "1688404671"
    }
    

    5. 前端上传到OSS

    拿到第四步的JSON后,请求JSON中的host,key = dir + 文件名.后缀,然后按照下图方式请求
    image
    注意

    1. Content-Type要为multipart/form-data;
    2. file 必须在表单数据中最后一个
    3. 上传的file大小不能超过5 GB
    4. 不知道为什么 我这边postman上传的file,它的文件名字不能是中文的,不然就会返回405,这个问题调试我半天,我觉得应该是字符编码哪里要设置一下,没有具体研究下去了
  • 相关阅读:
    如何实现 add[1][2][3] + 4 === 6?
    dfs之字符串拼接
    累加出整个范围所有的数最少还需要几个数
    中海达智能驾驶定位方案白皮书
    小芯片chiplet技术杂谈
    nvm 安装后出现的各种问题解决方法
    [附源码]Python计算机毕业设计Django学生在线考试系统
    四十三、Fluent增强收敛性-伪瞬态计算
    uniapp 搜索内容关键字高亮
    分布式定时调度:xxl-job 最佳实践详解
  • 原文地址:https://www.cnblogs.com/isyues/p/17524616.html