学相伴接入OSS后访问速度快了10倍

Posted 狂神说

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学相伴接入OSS后访问速度快了10倍相关的知识,希望对你有一定的参考价值。

问题场景

一个thymeleaf项目,上传图片、视频是直接使用Java代码保存到服务器上

于是服务器压力激增,网页静态资源变得卡顿

于是我们需要将图片、视频上传移植到阿里云OSS上

阿里云对象存储OSS(Object Storage Service)是阿里云提供的海量、安全、低成本、高持久的云存储服务。

一句话描述:它是一个很棒的存取文件的在线服务器

我们可以使用它进行客户端上传,文件不通过服务端也可以上传,这样减轻了我们服务端压力

我们需要一系列配置才可以对接完成,查阅阿里云OSS官方文档发现:javascript客户端签名直传的方式最为简单,但这种方式是有弊端的,客户端通过JavaScript把存取文件的认证信息写在代码里面有泄露的风险

因此我们采用服务端签名后直传的方式进行上传文件,我们待会儿介绍~

准备工作

登录阿里云官网

将鼠标移至产品,单击对象存储 OSS,打开 OSS 产品详情页面。

在 OSS 产品详情页,单击立即开通。

开通服务后,在 OSS 产品详情页单击管理控制台直接进入 OSS 管理控制台界面。

您也可以单击位于官网首页右上方菜单栏的控制台,进入阿里云管理控制台首页,然后单击左侧的对象存储 OSS 菜单进入 OSS 管理控制台界面。

根据官方文档提供步骤视频可以完成开通、创建Bucket、进行授权、配置跨域等 我们到阿里云RAM访问控制台来添加一个用户

添加用户

填入用户账号信息、勾选编程访问后点击确定

填入信息

然后我们点击复制,找个地方粘贴下来我们的AccessKey ID以及AccessKey Secret

复制密钥

然后我们点击添加权限为该用户授权

添加权限
选择权限
授权完成

开始对接

官方文档流程图片:
图片介绍

这里可以这么理解:

假设小王接到老板的任务:将这堆货物放到仓库中去,但仓库大门的门禁需要门卡

所以上图中的类比

1:小王去保管室拿门卡

2:小王拿到门卡回来

3:小王搬运货物到仓库,存放货物

因此我们首先先做“保管室”的功能

后端代码实现

这里就是用我们的Java代码在服务端实现:

引入alicloud-oss的依赖

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
            <version>2.2.0.RELEASE</version>
        </dependency>

application.yml,注意版本或者依赖不同,配置有可能改变,例如我最下面的Demo使用的boot的依赖(配置方式发生了变化,不过我们代码都还是一样的,只是注意@Value需要写对)

spring:
  cloud:
    alicloud:
      access-key: <你的AccessKey>
      secret-key: <你的SecretKey>
      oss:
        endpoint: <你的endpoint(可以在你的OSS控制台概览看到)>
        bucket: <你的bucket(可以在你的OSS控制台概览看到)>

Controller

package com.ruben.simpleoss.controller;
import com.ruben.simpleoss.service.OssService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Map;
/**
 * 上传Controller
 *
 * @author <achao1441470436@gmail.com>
 * @since 2021/6/18 17:40
 */
@RestController
public class UploadController 
    @Resource
    private OssService ossService;
    /**
     * oss上传文件
     *
     * @author <achao1441470436@gmail.com>
     * @since 2021/6/17 10:07
     */
    @GetMapping("oss")
    public Map<String, Object> oss() 
        return ossService.getMark();
    

Service

package com.ruben.simpleoss.service;
import java.util.Map;
/**
 * Oss服务层
 *
 * @author <achao1441470436@gmail.com>
 * @since 2021/6/17 10:21
 */
public interface OssService 
    Map<String, Object> getMark();

ServiceImpl

package com.ruben.simpleoss.service.impl;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.ruben.simpleoss.service.OssService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
 * @ClassName: UploadServiceImpl
 * @Description: 获取OSS签证
 * @Date: 2020/6/3 21:55
 * *
 * @author: achao<achao1441470436 @ gmail.com>
 * @version: 1.0
 * @since: JDK 1.8
 */
@Service
public class OssServiceImpl implements OssService 
    @Resource
    OSS ossClient;
    @Value("$spring.cloud.alicloud.oss.endpoint")
    private String endpoint;
    @Value("$spring.cloud.alicloud.oss.bucket")
    private String bucket;
    @Value("$spring.cloud.alicloud.access-key")
    private String accessId;
    /**
     * 获取临时签证
     *
     * @return
     */
    @Override
    public Map<String, Object> getMark() 
        String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint
        // callbackUrl为 上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
//        String callbackUrl = "http://88.88.88.88:8888";
        String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        String dir = format + "/"; // 用户上传文件时指定的前缀。
        Map<String, String> respMap = null;
        try 
            long expireTime = 3000;
            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(StandardCharsets.UTF_8);
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = ossClient.calculatePostSignature(postPolicy);
            respMap = new LinkedHashMap<String, String>(8);
            respMap.put("accessid", accessId);
            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());
         finally 
            ossClient.shutdown();
        
        Map<String, Object> result = new HashMap<>(4);
        result.put("data", respMap);
        result.put("code", 20000);
        return result;
    

然后我们运行访问一下,就像第一步小王去拿门卡了

可以看到拿到我们的临时凭证了,就像第二步小王拿着门卡回来了

访问接口

然后我们现在要拿着这些凭证去上传文件,就像第三步小王要去存货物

按照PostObject的文档测试一下

上传完成之后我们这里是没有回调的,因为我们这里没有配置callback,如有需要,可以查看Callback文档

postman

上传完成之后我们可以在控制台看到我们上传的文件

上传完成看控制台

然后如果我们在代码里使用文件的url的话,就使用host+/+(key替换掉$filename占位符为文件名)即可

例如我的hosthttps://will-deprecated.oss-cn-chengdu.aliyuncs.com

加上/再加上key为:2021-06-18/17585793-95b9-4dbd-8dfc-2302d6afa5df_$filename替换掉文件名就是2021-06-18/17585793-95b9-4dbd-8dfc-2302d6afa5df_partnerhead.jpg(其实我测试过了,不用$filename占位符,直接使用文件名也可以上传成功)

最终https://will-deprecated.oss-cn-chengdu.aliyuncs.com/2021-06-18/17585793-95b9-4dbd-8dfc-2302d6afa5df_partnerhead.jpg则是我们完整的文件路径

前端代码实现

使用html+vue

主页面,引用组件的地方

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>OSS上传Demo</title>
    <link href="/css/elementUI.css" rel="stylesheet">
    <script src="/js/jquery-3.5.1.min.js"></script>
    <script src="/js/vue.min.js"></script>
    <script src="/js/elementUI.js"></script>
    <script src="/js/singleUpload.js"></script>
</head>
<body>
<div id="myapp">
    <single-upload ref="singleUpload" v-model="imageUrl"></single-upload>
    文件url: imageUrl 
</div>
<script>
    new Vue(
        el: '#myapp',
        data: 
            imageUrl: ''
        
    )
</script>
</body>
</html>

自己写的组件:

function policy() 
    return new Promise((resolve, reject) => 
        $.ajax(
            url: '/oss',
            type: 'GET',
            contentType: 'application/json; charset=UTF-8',
            success: function (res) 
                if (res.code == 20000) 
                    resolve(res)
                 else 
                    reject(res)
                
            ,
            error: function (res) 
                reject(res)
            
        );
    );

Vue.component('singleUpload', 
    props: ['value'],
    template: '  <div class="single-upload">' +
        '    <el-upload' +
        '        :before-upload="beforeUpload"' +
        '        :data="dataObj"' +
        '        :file-list="fileList"' +
        '        :multiple="false"' +
        '        :on-preview="handlePreview"' +
        '        :on-remove="handleRemove"' +
        '        :on-success="handleUploadSuccess"' +
        '        :show-file-list="showFileList"' +
        '        action="dataObj.host"' +
        '        list-type="text"' +
        '        style="display: flex;"' +
        '    >' +
        '    <el-button size="small" type="primary">点击上传</el-button>' +
        '    </el-upload>' +
        '    <el-dialog :modal="false" :visible.sync="dialogVisible">' +
        '      <img width="100%;" v-if="isImg(fileList[0].url)" :src="fileList[0].url" alt="图片找不到了..."/>' +
        '      <video width="900px" controls autoplay muted v-if="isVideo(fileList[0].url)" :src="fileList[0].url" alt="视频找不到了..."/>' +
        '    </el-dialog>' +
        '  </div>',
    data() 
        return 
            dataObj: 
                policy: "",
                signature: "",
                key: "",
                ossaccessKeyId: "",
                dir: "",
                host: ""
                // callback:'',
            ,
            dialogVisible: false
        ;
    ,
    computed: 
        imageUrl() 
            return this.value;
        ,
        imageName() 
            if (this.value != null && this.value !== "") 
                return this.value.substr(this.value.lastIndexOf("/") + 1);
             else 
                return null;
            
        ,
        fileList() 
            return [
                
                    name: this.imageName,
                    url: this.imageUrl
                
            ];
        ,
        showFileList: 
            get: function () 
                return (
                    this.value !== null && this.value !== "" && this.value !== undefined
                );
            ,
            set: function (newValue) 
            
        
    ,
    methods: 
        isVideo() 
            let fileType = this.getFileType()
            return ~['.mp4', '.avi'].indexOf(fileType)
        ,
        isImg() 
            let fileType = this.getFileType()
            return ~['.png', '.jpg', '.jpeg', '.gif'].indexOf(fileType)
        ,
        getFileType() 
            let fileType = this.value.substring(this.value.lastIndexOf('.'))
            return fileType
        ,
        getUUID()  //生成UUID
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => 
                return (c === 'x' ? (Math.random() * 16 | 0) : ('r&0x3' | '0x8')).toString(16)
            )
        ,
        emitInput(val) 
            this.$emit("input", val);
        ,
        handleRemove(file, fileList) 
            this.emitInput("");
        ,
        handlePreview(file) 
            this.dialogVisible = true;
        ,
        beforeUpload(file) 
            let _self = this;
            return new Promise((resolve, reject) => 
                policy()
                    .then(response => 
                        console.log(response)
                        _self.dataObj.policy = response.data.policy;
                        _self.dataObj.signature = response.data.signature;
                        _self.dataObj.ossaccessKeyId = response.data.accessid;
                        _self.dataObj.key = response.data.dir + this.getUUID() + "_$filename";
                        _self.dataObj.dir = response.data.dir;
                        _self.dataObj.host = response.data.host;
                        resolve(true);
                    )
                    .catch(err => 
                        reject(false);
                    );
            );
        ,
        handleUploadSuccess(res, file) 
            console.log("上传成功...");
            this.showFileList = true;
            this.fileList.pop();
            this.fileList.push(
                name: file.name,
                url:
                    this.dataObj.host +
                    "/" +
                    this.dataObj.key.replace("$filename", file.name)
            );
            this.emitInput(this.fileList[0].url);
            console.log(this.fileList[0]);
        
    
)

注意引入一些文件:

引入文件

最终效果:

演示效果1
演示效果2
演示效果3
演示效果4
演示效果5

项目源码

本次案例完整项目代码:https://gitee.com/VampireAchao/simple-oss.git

这个项目基本能应对绝大部分场景,它那次请求签证接口是使用的jQueryajax

如果您是纯vue项目,可以看我以往这个项目https://gitee.com/VampireAchao/my-vue-app.git

小结

通过对这次的OSS对接,复习了如何在html页面中引入vueelementUI、引入vue自定义组件、进行第三方服务接入等...

大部分项目目前都接入了OSS,所以这是一个必须要掌握的技能!

文章编辑:学相伴阿超

KuangStudy首发:https://www.kuangstudy.com/bbs/1405846273780682753


看完记得点个在看吧,这对我们很重要哦~


长按扫码关注平台最新动态

以上是关于学相伴接入OSS后访问速度快了10倍的主要内容,如果未能解决你的问题,请参考以下文章

加油站小王学完这份阿里大牛分享的685页分布式系统开发实战文档,直接入职蚂蚁

电脑慢的快速解决办法 22种方法电脑速度变快10倍

学相伴微信支付V3版实战课上线

接入阿里云OSS--公有云分布式存储

接入阿里云OSS--公有云分布式存储

接入阿里云OSS--公有云分布式存储