zoukankan      html  css  js  c++  java
  • 阿里云平台OSS对象存储

    OSS即“OpenStorageService”,概念上没啥新意,就是本地存储搬到阿里云平台上了,单个存储对象大小可以达到5G,看了下阿里的OSS教程java版本,

    使用原生js和servlet实现,这基本就能看不能用,为了做点通用的,我们来玩一把OSS,抓下时髦的尾巴。

    准备:

    Idea2019.03/Gradle6.0.1/JDK11.0.4/Lombok0.28/SpringBoot2.2.4RELEASE/mybatisPlus3.3.0/Soul2.1.2/Dubbo2.7.5/Mysql8.0.11/Vue2.5/OSS

    难度: 新手--战士--老兵--大师

    目标:

    1.Vue前端+Java后端结合实现OSS图片对象存储

    步骤:

    为了遇见各种问题,同时保持时效性,我尽量使用最新的软件版本。代码地址:https://github.com/xiexiaobiao/vehicle-shop-admin

    1 OSS简介

    阿里云官网资料太多,请君移步,我就不搬运了。

    2 分析原理

    很多时候,我们上传文件都是前端把待传数据先给后端应用服务器,然后由后端Server再分类存储到比如Mysql,Mongo等等,由于现代的前端功能已十分强大,

    当然可以直接由前端上传文件直接到OSS,后端Server只需要告诉上传到哪里以及如何上传即可,这样可以降低后端负载, 流程如下:

     

    1. 用户每次需要上传时,web端先向应用服务器请求上传Policy(就是OSS存储地址和目录、账号密码信息、签名、过期信息等)和                                                                                                                      回调(上传过程和结果信息反馈给需要的服务器,做展示/记录等用,户上传完文件后,不会直接得到返回结果,而是先通知应用服务器,再把结果转达给用户)。
    2. 应用服务器返回给web端上传Policy和回调设置。
    3. web端直接向OSS发送文件上传请求,并上传文件。
    4. OSS根据用户的回调设置,发送回调请求给应用服务器。
    5. 应用服务器返回响应给OSS。
    6. OSS将应用服务器返回的内容返回给web端。

    这里有个模式问题,其实如果把OSS相关的Policy信息直接写在前端代码里,然后直接上传文件,也可以的,只是安全性会让人心慌,算是方案一,

    如果仍然由后端提供Policy而不要回调信息,直接返回信息给web端就是方案二,上面的方式就是方案三了,我这里以方案三说事。

    3 看前端代码

    使用elementUI的upload组件,具体可以见elementUI的官方组件介绍,了解各属性含义:src/components/Upload/singleUpload.vue

        <el-upload
          ref="upload"
          :action="useOss?ossUploadUrl:minioUploadUrl"
          :limit="1"
          :data="useOss?dataObj:null"
          list-type="picture"
          :multiple="false"
          :file-list="fileList"
          :auto-upload= "autoUpload"
          :show-file-list="true"
          :before-upload="beforeUpload"
          :on-remove="handleRemove"
          :on-success="handleUploadSuccess"
          :on-preview="handlePreview">
          <el-button slot="trigger" size="small" type="primary">选取文件</el-button>
          <!--以下为如果手动触发上传的按钮-->
     <!-- <el-button style="margin-left: 10px;" size="small" type="success" @click="submitUpload">上传到服务器</el-button>-->
          <div slot="tip" class="el-upload__tip">只能上传一张jpg/png文件,且不超过10MB</div>
        </el-upload>

    上面代码的核心要点:

    • 并通过:auto-upload设置为自动提交,即只要选择好文件后就自动提交,当然也可以先选好文件,再通过按钮触发提交,见我注释掉的部分。
    • :before-upload="beforeUpload" 即真实开始上传文件前做的工作:
    beforeUpload(file) {
            let _self = this;
            if(!this.useOss){
              //不使用oss不需要获取策略
              returntrue;
            }
            returnnewPromise((resolve, reject) => {
              policy().then(response => {
                // 返回的对象,多一层data封装,故写为response.data.data
                _self.dataObj.policy = response.data.data.policy;
                _self.dataObj.signature = response.data.data.signature;
                _self.dataObj.ossaccessKeyId = response.data.data.accessKeyId;
                _self.dataObj.key = response.data.data.dir + '/'+this.fileNameUUID +'${filename}';
                _self.dataObj.dir = response.data.data.dir;
                _self.dataObj.host = response.data.data.host;
                // _self.dataObj.callback = response.data.data.callback;
                resolve(true)
              }).catch(err => {
                console.log(err)
                reject(false)
              })
            })
          },

    以上通过axios向后端发异步请求,policy()方法我放在其他文件中了,就是通过axios的GET访问去后端获取Policy,然后再赋值到当前的存储变量上,

    这样就取得了Policy信息,注意_self.dataObj.key这里的 '/' 不能放 '${filename}' 前,否则oss存储时会自动增加一层目录目录!!只要以上正确,基本就能自动完成上传了。

    4 看后端代码

    OSS专用的依赖:

    compile group: 'com.aliyun.oss', name: 'aliyun-sdk-oss', version: '3.8.1'
    compile group: 'com.aliyun', name: 'aliyun-java-sdk-core', version: '4.5.0'
    compile group: 'com.aliyun', name: 'aliyun-java-sdk-sts', version: '3.0.1'

    com.biao.shop.stock.impl.AliyunOssServiceImpl, 即上面的前端请求Policy的处理对象,Controller我就不展示了,就是响应前端REST,然后调用这个服务:

    @Service
    publicclass AliyunOssServiceImpl implements AliyunOssService {
    
        privatestaticfinal Logger LOGGER = LoggerFactory.getLogger(AliyunOssServiceImpl.class);
        @Value("${aliyun.oss.policy.expire}")
        privateint ALIYUN_OSS_EXPIRE;
        @Value("${aliyun.oss.maxSize}")
        privateint ALIYUN_OSS_MAX_SIZE;
        @Value("${aliyun.oss.callback}")
        private String ALIYUN_OSS_CALLBACK;
        @Value("${aliyun.oss.bucketName}")
        private String ALIYUN_OSS_BUCKET_NAME;
        @Value("${aliyun.oss.endpoint}")
        private String ALIYUN_OSS_ENDPOINT;
        @Value("${aliyun.oss.dir.prefix}")
        private String ALIYUN_OSS_DIR_PREFIX;
    
        private OSSClient ossClient;
    
        @Autowired
        public AliyunOssServiceImpl(OSSClient ossClient) {
            this.ossClient = ossClient;
        }
    
        @Override
        public ObjectResponse<AliyunPolicy> policy() {
    
            ObjectResponse<AliyunPolicy> result = new ObjectResponse<>();
            // 存储目录,按天区分
            DateTimeFormatter dtf =   DateTimeFormatter.ofPattern("yyyy/MM/dd");
            String dir = ALIYUN_OSS_DIR_PREFIX + LocalDateTime.now().toLocalDate().format(dtf);
            // 签名有效期
            long expireEndTime = System.currentTimeMillis() + ALIYUN_OSS_EXPIRE * 1000;
            Date expiration = new Date(expireEndTime);
            // 文件大小
            long maxSize = ALIYUN_OSS_MAX_SIZE * 1024 * 1024;
            // 回调
            AliyunCallbackParam callback = new AliyunCallbackParam();
            callback.setCallbackUrl(ALIYUN_OSS_CALLBACK);
            callback.setCallbackBody("filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");
            callback.setCallbackBodyType("application/x-www-form-urlencoded");
            // 提交节点
            String action = "http://" + ALIYUN_OSS_BUCKET_NAME + "." + ALIYUN_OSS_ENDPOINT;
            try {
                PolicyConditions policyConds = new PolicyConditions();
                policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize);
                policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
                String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
                byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
                String policy = BinaryUtil.toBase64String(binaryData);
                String signature = ossClient.calculatePostSignature(postPolicy);
                // 对"callback"属性进行base64编码编码
                String callbackData = BinaryUtil.toBase64String(
                        JacksonUtil.convertToJson(callback).getBytes(StandardCharsets.UTF_8));
                // 返回结果
                AliyunPolicy aliyunPolicy = new AliyunPolicy();
                aliyunPolicy.setAccessKeyId(ossClient.getCredentialsProvider().getCredentials().getAccessKeyId());
                aliyunPolicy.setPolicy(policy);
                aliyunPolicy.setSignature(signature);
                aliyunPolicy.setExpire(String.valueOf(expireEndTime));
                aliyunPolicy.setDir(dir);
                aliyunPolicy.setCallback(callbackData);
                aliyunPolicy.setHost(action);
                result.setCode(RespStatusEnum.SUCCESS.getCode());
                result.setMessage(RespStatusEnum.SUCCESS.getMessage());
                result.setData(aliyunPolicy);
            } catch (Exception e) {
                LOGGER.error("signature failed !!", e);
            }
            return result;
        }
    }

    以上我做了个响应的对象封装,ObjectResponse{code,message,data},关于OSS的账号密码信息,我都放在配置文件统一管理;

    注意OSSClient要通过Configuration类中生成一下。可以看到以上设置了一系列的OSS存储政策(Policy):存储目录,按天区分;签名有效期;文件大小限制;

    回调地址;提交上传的OSS地址等等。

    5 再看前端代码:

    src/components/Upload/singleUpload.vue:

    这是处理每次上传文件完成之后的逻辑,如果设置为可以上传多个文件,那么这个会执行多次。因为OSS存储,如果同目录下文件同名就直接覆盖,

    它不会询问大家意见的!所以这里有个小技巧:我通过getFileNameUUID自动生成个随机前缀,绑定到一个本地变量,每次上传成功,都刷新一下,

    再混入url和key值,perfect!!

    handleUploadSuccess(res, file) {
            this.showFileList = true;
            this.fileList.pop();
            let url = this.dataObj.host + '/' + this.dataObj.dir +  '/' + this.fileNameUUID+ file.name;
            if(!this.useOss){
              //不使用oss直接获取图片路径
              url = res.data.url;
            }
            this.fileList.push({name: this.fileNameUUID + file.name, url: url});
            // 每次上传完成,更新fileNameUUID
            this.getFileNameUUID();
            this.emitInput(this.fileList[0].url);
          },
          /*随机生成文件前缀,防止同名覆盖*/
          getFileNameUUID(){
            this.fileNameUUID = (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(0,4);
          },

    效果展示,上传中,可以看到进度:

    完成后,可以直接删除,或者查看大图,如果做的完美一点,点删除,要触发OSS删除事件,我代码省略没写啦。

     当然,还有些其他组件,如其他一些封装类Bean,我就不细说啦,直接看我代码即可!

    @Data
    publicclass AliyunPolicy {
        private String AccessKeyId;
        private String host;
        private String policy;
        private String signature;
        private String expire;
        private String callback;
        private String dir;
    }

    后记:

    1,我买了一套阿里的云产品:ECS 弹性计算服务器,RDS云关系DB,OSS对象存储,把我自己开发的一套前后端都部署上了,

    体验就是真羡慕有钱人,起步价的机器比如2G内存,上5个微服务基本就跑满了!在想尽了各种省内存办法又没辙的情形下,我

    只能又拉了一台ECS,才架起了网关,注册中心,内存消耗记录:Nacos 0.4G,zookeeper 0.1G,微服务模块5个:authority 200M,

    customer 200M,stock 200M,order 200M,business 200M,其他Nginx 100M,souladmin 0.2G,soulbootstrap 0.2G,另外就是

    传说Jetty比Tomcat省内存,反正我没看出来,估计是服务数量级不够

    2,如果公司money多,还是推荐上这些云服务的,至少不会上演最近的微盟删库但跑路不成功的事件,云服务自动有各种容灾备份

    机制,如异地备,定期备,不亦乐乎?

    3,关于callback没细说,具体看代码吧,代码是最最好的老师。

     

    全文完!


    推荐阅读:

     

  • 相关阅读:
    jmeter学习笔记(3)-jmeter结合fiddler
    jmeter学习笔记(2)—http信息头管理器+断言
    requests接口自动化9-共享session和传递cookie
    djangorestframework学习1-通过HyperlinkedModelSerializer,ModelViewSet,routers编写第一个接口
    requests接口自动化8-传递数据为xml形式的post请求:data
    requests接口自动化7-Multi/form-data文件上传形式的post请求:files
    requests接口自动化6-Body里json格式数据形式的post请求:json
    requests接口自动化5-表单参数形式的post请求:data
    requests接口自动化4-登录后才能访问的get请求,需共享cookie
    requests接口自动化3-url里带参数的get请求:params
  • 原文地址:https://www.cnblogs.com/xxbiao/p/12381598.html
Copyright © 2011-2022 走看看