• 文件存储解决方案-云存储阿里 OSS


    文件存储解决方案-云存储阿里 OSS

    1.文件存储(上传)解决方案讨论

    1.图解

    • 文件存储解决方案-云存储阿里 OSS

    image-20221113152120004

    解读上图

    1. 普通上传并不是分布式,也不是集群,可用性不高
    2. 普通上传的分布式情况,使用了集群,但是当把文件存储在集群中的某台服务器,当下次读取时,因为负载均衡,存在读取不到的文件问题
    3. 应用服务器使用集群,文件存储通过网关提供统一接口来存储文件,可用性高
      3.1 方案 1: 自建服务器,成本高, 也存在技术瓶颈
      3.2 方案 2: 使用云存储产品(阿里云等), 是企业优选方案

    2.对应 分布式 微服务 架构分析

    2.阿里云对象存储介绍

    1.官网

    https://www.aliyun.com/product/oss

    2.说明

    阿里云对象存储 OSS(Object Storage Service)是一款海量、安全、低成本、高可靠的云存储服务,提供数据高可用性, 多种存储类型供选择,全面优化存储成本

    3.上传方式

    1.普通上传方式

    1.阿里云对象存储-普通上传示意图

    image-20221113152537146

    2.分析

    • 上传慢:用户数据需先上传到应用服务器,之后再上传到OSS。网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。

    • 扩展性差:如果后续用户多了,应用服务器会成为瓶颈。

    • 费用高:需要准备多台应用服务器。由于OSS上传流量是免费的,如果数据直传到OSS,
      不通过应用服务器,那么将能省下几台应用服务器

    2.服务端签名后直传

    1.阿里云对象存储-服务端签名后直传示意图

    image-20221113152649133

    2.分析

    • Web端向服务端请求签名,然后直接上传,不会对服务端产生压力,而且安全可靠。
    • 但服务端无法实时了解用户上传了多少文件,上传了什么文件。如果想实时了解用户
      上传了什么文件,可以采用服务端签名直传并设置上传回调

    3.准备工作

    1.注册一个阿里云账号,并完成认证

    2.阿里云地址:https://www.aliyun.com/

    3.创建阿里云对象 Bucket 创建并测试

    4.创建 RAM 用户(访问控制 RAM(Resource Access Management)是阿里云提供的一项管理用户身份与资源访问权限的服务), 得到 accessKeyId 和 accessKeySecret (可以理解成是阿里云子用户的 id 和密码)

    具体步骤可以参考https://llinp.cn/articles/2022/01/02/1641105914205.html

    5.将得到 endpoint , accessKeyId , accessKeySecret 填写到代码中, 并指定要上传的文
    件路径, 阿里云哪个 Bucket 和上传后的文件名是什么

    4.使用原生 SDK,上传文件到阿里云对象 Bucket

    参考阿里云官方文档 https://help.aliyun.com/document_detail/84781.html#p-yqj-z1w-rl2

    ,这里我使用的是简单上传-上传文件流的方式。

    1.创建一个boot项目引入依赖

    <dependency>
        <groupId>com.aliyun.ossgroupId>
        <artifactId>aliyun-sdk-ossartifactId>
        <version>3.5.0version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.编写如下一个测试类,进行测试。

    @RestController
    public class TestController {
    
        @RequestMapping("/test")
        public R test() {
            // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
            String accessKeyId = "你的accessKeyId";
            String accessKeySecret = "你的accessKeySecret";
            // 填写Bucket名称,例如examplebucket。
            String bucketName = "你的bucketName";
            // 上传文件名 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
            String objectName = "12.jpg";
            // 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
            // 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。
            String filePath = "C:\\Users\\llp\\Desktop\\solo-fetchupload-7208404724819002506-m0Dvrtu.jpg";
    
            // 创建OSSClient实例。
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    
            try {
                //文件输入流
                InputStream inputStream = new FileInputStream(filePath);
                // 创建PutObject请求。 将文件流写入到objectName文件中
                ossClient.putObject(bucketName, objectName, inputStream);
            } catch (OSSException oe) {
                System.out.println("Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.");
                System.out.println("Error Message:" + oe.getErrorMessage());
                System.out.println("Error Code:" + oe.getErrorCode());
                System.out.println("Request ID:" + oe.getRequestId());
                System.out.println("Host ID:" + oe.getHostId());
            } catch (ClientException ce) {
                System.out.println("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.");
                System.out.println("Error Message:" + ce.getMessage());
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } finally {
                if (ossClient != null) {
                    ossClient.shutdown();
                }
            }
            return R.ok();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    image-20221113164918485

    可以看到文件已经上传到阿里云oss服务器上了

    image-20221113165057664

    5.使用 SpringCloud Alibaba OSS 传文件到阿里云对象 Bucket

    1.引入依赖

    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alicloud-ossartifactId>
        <version>2.1.0.RELEASEversion>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.编写yaml

    spring:
      datasource:
        username: root
        password: root
        url: jdbc:mysql://192.168.56.100:3306/llpliving_commodity?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
        driver-class-name: com.mysql.jdbc.Driver
      cloud:
        alicloud:
          oss:
            endpoint: 参考创建bucket时选择的归属区域
          access-key: 你的keyId
          secret-key: 你的keySecret
          bucket-name: 你的bucketName
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.编写测试类

        @Resource
        private OSSClient ossClient;
    
        @Value("${spring.cloud.alicloud.bucket-name}")
        private String bucketName;
        //上传指定的文件到bucket
        @RequestMapping("/test2")
        public R test2() throws FileNotFoundException {
            String filePath = "C:\\Users\\asus\\Desktop\\solo-fetchupload-7208404724819002506-m0Dvrtu.jpg";
            String objectName = "13.jpg";
            InputStream inputStream = new FileInputStream(filePath);
            ossClient.putObject(bucketName, objectName, inputStream);
            ossClient.shutdown();
            return R.ok();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    image-20221113182803195

    6.完成服务端签名后直传

    1.文档

    https://help.aliyun.com/document_detail/31926.html

    image-20221113182941885

    2.代码示例

    https://help.aliyun.com/document_detail/91868.htm

    3.代码+配置实现

    1.引入依赖

    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alicloud-ossartifactId>
        <version>2.1.0.RELEASEversion>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.编写yaml

    server:
      port: 7070
    
    spring:
      cloud:
        alicloud:
          oss:
            endpoint: oss-cn-chengdu.hangzhou.com
          access-key: 你的accessKey
          secret-key: 你的secretKey
          bucket-name: 你的bucketName
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3.配置类,从yaml配置文件中读取配置信息

    @Data
    @Component
    @ConfigurationProperties("spring.cloud.alicloud")
    public class OssProperties implements InitializingBean {
    
        @Value("${spring.cloud.alicloud.oss.endpoint}")
        private String endpoint;
        private String accessKey;
        private String secretKey;
        private String bucketName;
    
        public static String ENDPOINT;
        public static String KEY_ID;
        public static String KEY_SECRET;
        public static String BUCKET_NAME;
    
        @Override
        public void afterPropertiesSet() throws Exception {
            this.ENDPOINT = endpoint;
            this.KEY_ID = accessKey;
            this.KEY_SECRET = secretKey;
            this.BUCKET_NAME = bucketName;
            this.ENDPOINT = endpoint;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    4.编写测试类

    @RestController
    public class OssServiceController {
    
        //提示:这里的注入方式是 OSS 接口注入, 不要写成实现类了
        @Resource
        private OSS ossClient;
    
        /**
         * 获取文件上传签名/授权
         * 这段代码从阿里云示例文档拷贝, 并做修改,去掉暂时不用的代码
         *
         * @return
         */
        @RequestMapping("/oss/policy")
        public R policy() {
            // 填写Host地址,格式为https://bucketname.endpoint。
            String host = "https://" + OssProperties.BUCKET_NAME + "." + OssProperties.ENDPOINT;
            // 设置上传到OSS文件的前缀,可置空此项。置空后,文件将上传至Bucket的根目录下。
            // 我们可以将文件按照 年-月-日的形式分目录存放在阿里云
            String format = new SimpleDateFormat("yyyy/MM/dd").format(new Date());
            // 用户上传文件时指定的前缀。
            String dir = format + "/";
            // 创建ossClient实例。
            OSS ossClient = new OSSClientBuilder().build(OssProperties.ENDPOINT, OssProperties.KEY_ID, OssProperties.KEY_SECRET);
            Map<String, String> respMap = null;
            try {
                long expireTime = 30;
                long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
                Date expiration = new Date(expireEndTime);
                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", OssProperties.KEY_ID);
                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));
    
    
            } catch (Exception e) {
                // Assert.fail(e.getMessage());
                System.out.println(e.getMessage());
            }
            return R.ok().put("data", respMap);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    5.测试

    浏览器: http://localhost:7070/oss/policy

    image-20221116191313271

    6.前端拿到policy凭证之后,通过policy直接上传到阿里云

    image-20221116195005316

    import http from '@/utils/httpRequest.js'
    export function policy() {
       return  new Promise((resolve,reject)=>{
            http({
                //url: http.adornUrl("/oss/policy"),
                url: "http://localhost:7070/oss/policy",
                method: "get",
                params: http.adornParams({})
            }).then(({ data }) => {
                resolve(data);
            })
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    7.前端拿到policy直接进行上传会存在跨域问题

    image-20221116194409876

    8.解决阿里云跨域问题, 设置跨域

    数据安全-跨域设置

    image-20221116195330606

    9.再次上传,依然存在问题

    image-20221116195425778

    image-20221116195642252

    10.再次上传,上传成功

    image-20221116195717538

  • 相关阅读:
    单例模式--Java
    汽车射频之基础
    构建 Active Directory 域的最佳实践
    Python 实现获取【昨天】日期
    【计算机图形学入门】笔记3:变换Transformation(二维与三维)
    python 列表常用方法
    C语言:生成校验码
    接口测试 —— Jmeter 之测试片段的应用
    前端发布项目后,解决缓存的老版本文件问题
    Tensorflow模型各部分自定义方式
  • 原文地址:https://blog.csdn.net/qq_44981526/article/details/127892160