springboot整合OSS阿里云上传文件(上传回调方式)


一、阿里oss服务的开通

可以参考下这个博客,记录的基本完全 ==> 文章入口

小编说几个注意点

1、开启oss端的跨域步骤如下

在这里插入图片描述
在这里插入图片描述
进来后点击设置
在这里插入图片描述
在这里插入图片描述

2、权限的读写配置

在这里插入图片描述

3、创建子AccessKey

很重要:在使用中,创建子AccessKey,这样安全,记得给他配置响应的权限,例如oss权限


二、如何从文档入手编程

01、阿里oss官网示例文档 ==> 入口
02、Alibaba Cloud OSS Example提供的示例 ==>入口

有那些比较重点的如图:
在这里插入图片描述
在github上,看示例(如果github缓慢,或者连接不了,自行百度教程,更改下本地的配置)
在这里插入图片描述

三、小编的案例是服务端签名直传并设置上传回调

3.1、什么意思?

  1. 不将文件上传到后端
  2. 访问后端接口生成密钥,前端拿密钥,将文件直接上传到阿里云储存
  3. 好处:后端就不需要读取文件速度快,使用密钥也安全
  4. 言归正传直接上案例说明

代码如下(示例):

3.2、pom文件引入依赖

说明:注意springcloud版本和springboot版本要适配起来

详情看小编这篇博客==>文章入口

简单图示一下:
在这里插入图片描述
完整的pom文件==>小编使用的版本是2.1.8.RELEASE+Greenwich.SR3

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>

        <!--版本-->
        <version>2.1.8.RELEASE</version>

        <relativePath/> 
    </parent>
    <groupId>sqy</groupId>
    <artifactId>springboot_oss</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot_oss</name>
    <description>整合Spring-Cloud-Alibaba的文件上传-OSS</description>
    
    <properties>
    	<!--jdk版本-->
        <java.version>1.8</java.version>
        <!--版本-->
        <spring-cloud.version>Greenwich.SR3</spring-cloud.version>
    </properties>
    
    <dependencies>
        <!--oss依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
        </dependency>
        
        <!-- web应用-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!--test-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <!--版本控制-->
    <dependencyManagement>
        <dependencies>
            <!-- springcloud-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            
            <!-- spring-cloud-alibaba-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3.3、在application.yml配置相关的数据

小编采用的是yml格式,也可以使用application.properties

spring:
  cloud:
    alicloud:
      access-key: 你的ak
      secret-key: 你的sk
      oss:
        endpoint: 你的对外服务的访问域名
        bucket:   你的存储空间
  #应用名称
  application:
    name: springboot_OSS
#端口
server:
  port: 8888
  1. ak和sk就是你申请的了(最好是创建子AccessKey)
  2. 如何看endpoint(对外服务的访问域名)==> 看选择的地域 ==>查看入口

在这里插入图片描述
在这里插入图片描述

3.4、创建OSSController生成密钥

为什么是注入是 OSS 而不是 OssClient 原因请看小编这篇文章==>文章入口

在测试类中注入OssClient 是不会报错的,在controller会

package sqy;

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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author suqinyi
 * @Date 2021/5/9
 */

/**
 * 说明:
 * 官网介绍:https://help.aliyun.com/document_detail/91868.htm
 */
@RestController
public class OSSController {

    @Resource
    @Autowired(required = false)//单纯去掉红色下划线,强迫症
    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;

    @RequestMapping("/oss/policy")
    public Map<String, String> policy() {
        //https://gjsbzll-sqhzqy.oss-cn-beijing.aliyuncs.com/dog.png?versionId=ChNWE2OGQ0ZjczYy
        String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint
        // callbackUrl为 上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
        String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        String callbackUrl = "http://88.88.88.88:8888";
        String dir = format+"/"; // 用户上传文件时指定的前缀。
        Map<String, String> respMap=null;
        try {
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            // PostObject请求最大可支持的文件大小为5 GB,即CONTENT_LENGTH_RANGE为5*1024*1024*1024。
            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", 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();
        }
        System.out.println("respMap:"+respMap);
        return respMap;
    }
}

3.5、配置跨域

package sqy;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
 * @author suqinyi
 * @Date 2021/5/9
 * 实现基本的跨域请求
 */
@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration corsConfiguration = new CorsConfiguration();
        /*是否允许请求带有验证信息*/
        corsConfiguration.setAllowCredentials(true);
        /*允许访问的客户端域名*/
        corsConfiguration.addAllowedOrigin("*");
        /*允许服务端访问的客户端请求头*/
        corsConfiguration.addAllowedHeader("*");
        /*允许访问的方法名,GET POST等*/
        corsConfiguration.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
}

3.6、目录结构

在这里插入图片描述

3.7、测试生成密钥的接口

在这里插入图片描述

四、前端使用

4.1、说明

  1. 小编使用html文件,编写代码使用vuejs语法
  2. 前端页面小编设置是单文件上传,单独的组件
  3. 组件库使用element ui
  4. 发送请求使用axios和promise语法

4.2、页面效果

在这里插入图片描述

4.3、完整页面代码

说明:里面的组件通信是没有用到,因为小编这个写个案例,就怎么简单怎么来了

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>oss单文件文件上传</title>
  <!-- 链接引入需要联网 -->
  <script src=" https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <!-- 引入样式 (element ui) -->
  <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
  <!-- 引入组件库(element ui)-->
  <script src="https://unpkg.com/element-ui/lib/index.js"></script>
  <!-- axios发送异步请求 -->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
 
</head>

<body>
  <div id="app">
    <!-- 上传的组件 -->
    <div>
      <el-upload action="https://gulimall-sqy.oss-cn-beijing.aliyuncs.com" 
        :data="dataObj" 
        list-type="picture"
        :multiple="false" 
        :show-file-list="showFileList" 
        :file-list="fileList" 
        :before-upload="beforeUpload"
        :on-remove="handleRemove" 
        :on-success="handleUploadSuccess" 
        :on-preview="handlePreview">
        <el-button size="small" type="primary">点击上传</el-button>
        <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过10MB</div>
      </el-upload>
      <el-dialog :visible.sync="dialogVisible">
        <img width="100%" :src="fileList[0].url" alt="">
      </el-dialog>
    </div>

  </div>
</body>

<script type="text/javascript">
  //实例化vue
  new Vue({
    el: '#app',
    props: {
      value: String
    },
    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) {}
      }
    },
    data() {
      return {
        dataObj: {
          policy: '',
          signature: '',
          key: '',
          ossaccessKeyId: '',
          dir: '',
          host: '',
          // callback:'',
        },
        dialogVisible: false
      };
    },
    methods: {
      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) => {
          new Promise((resolve, reject)=>{
            axios({
              url: "http://localhost:8888/oss/policy",
              methods: 'get',
              //参数
              //params:{ }
            }).then(({data}) => {
              console.log("签名数据:",data);
              resolve(data);
            })
          }).then(response => {
            _self.dataObj.policy = response.policy;
            _self.dataObj.signature = response.signature;
            _self.dataObj.ossaccessKeyId = response.accessid;
            _self.dataObj.key = response.dir  + this.getUUID() + '_${filename}';
            _self.dataObj.dir = response.dir;
            _self.dataObj.host = response.host;
            resolve(true)
          }).catch(err => {
            reject(false)
            console.log("失败",err);
          })
        })
      },
      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)
        });
        console.log("访问地址:",this.fileList[0].url);
        this.emitInput(this.fileList[0].url);
      },
      //生成uuid
      getUUID () {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
          return (c === 'x' ? (Math.random() * 16 | 0) : ('r&0x3' | '0x8')).toString(16)
        })
      }
    }
  });
</script>

</html>

4.4、上传效果

在这里插入图片描述

4.5、oss上面的效果,文件在文件中

在这里插入图片描述

4.6、如何显示图片

使用img标签

<img src="图片访问地址">

<!-- 加个冒号可以动态绑定地址 -->
<img :src="图片访问地址">

五、如何清理oss上面不要的图片

实际操作小编就不写案例,小编说个思路

  1. 采用redis
  2. 上传图片的时候,将返回的图片地址,存入redis中
  3. 使用定时器,定时将数据库有用到的图片地图查询出来,存入redis
  4. 将所有的图片地址,和数据库的图片地址进行减法
  5. 在将那些减完剩下的图片地址,使用oss的移除功能将其删除

六、其实小编个人不推荐这种做法

推荐取消自动上传,将文件和数据一起提交到后台处理

好处:文件和数据一起传入后台处理,文件过大可以开多线程(异步)处理
这样的好处就是不会被恶意上传,浪费了OSS存储空间,毕竟存储空间是要收费的,文件在过大,我们也可以考虑压缩包的方式等…

完结。码字不易,小手点点

  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
ARM OSS 提供了自定义上传回调的功能,您可以通过设置上传回调URL,在文件上传时,ARM OSS 会向指定的 URL 发送 HTTP POST 请求,通知您文件上传的状态和相关信息。 要设置自定义上传回调,您需要完成以下步骤: 1. 创建一个用于接收上传回调请求的服务器或者服务。 2. 在 ARM OSS 管理控制台或者通过 SDK 创建文件上传任务时,设置上传回调 URL。 3. 在您的服务器或者服务中,编写处理上传回调请求的代码,根据请求中的信息,执行相关的操作(例如记录上传日志、通知上传完成等)。 以下是一个处理上传回调请求的示例代码: ``` const http = require('http'); http.createServer((req, res) => { let body = ''; req.on('data', chunk => { body += chunk.toString(); }); req.on('end', () => { const result = JSON.parse(body); // 处理上传回调请求,例如记录上传日志、通知上传完成等 console.log(result); res.end(); }); }).listen(3000); ``` 在上述示例代码中,我们创建了一个 HTTP 服务器,并监听 3000 端口。当收到上传回调请求时,我们将请求体解析为 JSON 对象,并输出到控制台,最后发送响应。 注意:为了保证上传回调的可靠性,您需要在服务器或者服务中处理可能的错误和异常,例如请求超时、网络错误等。您也可以在上传回调请求中返回 HTTP 状态码和错误信息,用于后续的处理和排查。 希望以上信息能够对您有所帮助。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

suqinyi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值