Spring Boot整合阿里云OSS文件上传

关注我,回复关键字“spring”免费领取Spring学习资料
来源:https://www.cnblogs.com/atwood-pan/p/17446138.html

一、注册阿里云账号并开通OSS服务

1、登录阿里云账号

 


2、创建一个bucket

3、创建子用户

对自用户分配权限,打开操作OSS的全部权限(也可根据业务需求进行更改)


4、配置上传跨域规则

  • 任何来源: *

  • 允许方法: POST

  • 任何请求头Headers: *

 

二、文件上传方式

1、服务器直传方式

每个OSS的用户都会用到上传服务。Web端常见的上传方法是用户在浏览器或App端上传文件到应用服务器,应用服务器再把文件上传到OSS

和数据直传到OSS相比,以上方法有三个缺点:

  • 上传慢:用户数据需先上传到到应用服务器,之后再上传到OSS。网络传输时间比直传到OSS多一倍。如果用于数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度

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

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

欢迎关注公众号:SpringForAll社区(spring4all.com),专注分享关于Spring的一切!回复“加群”还可加入Spring技术交流群!

2、服务端签名后前端直传

Web前端服务端请求签名,然后前端(Vue)直接上传,不会对服务端产生压力,而且安全可靠。

相关资料:服务端签名直传并设置上传回调概述

Java连接实例:Java实践OSS

 

上传回调流程

 

  1. Web前端请求应用服务器,获取上传所需参数(如OSS的accessKeyId、policy、callback等参数)

  2. 应用服务器返回相关参数

  3. Web前端直接向OSS服务发起上传文件请求

  4. 等上传完成后OSS服务会回调应用服务器的回调接口

  5. 应用服务器返回响应给OSS服务

  6. OSS服务将应用服务器回调接口的内容返回给Web前端

3、SpringBoot整合OSS实现文件上传

1、在pom.xml中添加相关依赖

    

<dependency>

<groupId>com.aliyun.oss</groupId>

<artifactId>aliyun-sdk-oss</artifactId>

<version>3.10.2</version>

</dependency>

2、修改SpringBoot配置文件

    

#操作oss需要的一些参数

aliyun:

oss:

accessKeyId: xxx # 阿里云的accessKeyId

accessKeySecret: xxx # accessKey 密码

endPoint: xxx # Endpoint:在阿里云oss控制台查看自己使用的endpoint,eg: oss-cn-shanghai.aliyuncs.com

bucketName: xxx # bucket 名称

policy:

expire: 300 # 签名有效期(S)

maxSize: 10 # 上传文件大小(M)

callback: http://localhost:8080/aliyun/oss/callback # 文件上传成功后的回调地址

dir:

prefix: xxx/images/ # 上传文件夹路径前缀

3、添加OSS的相关Java配置
用于配置OSS的连接客户端的OSSClient

    

/**

* TODO 用于配置OSS的连接客户端OSSClient

*

* @author ss_419

* @version 1.0

* @date 2023/5/28 19:04

*/

@Configuration

@Component

public class OssConfig {

@Value("${aliyun.oss.endpoint}")

private String ALIYUN_OSS_ENDPOINT;

@Value("${aliyun.oss.accessKeyId}")

private String ALIYUN_OSS_ACCESSKEYID;

@Value("${aliyun.oss.accessKeySecret}")

private String ALIYUN_OSS_ACCESSKEYSECRET;

@Bean

public OSSClient ossClient() {

return new OSSClient(ALIYUN_OSS_ENDPOINT, ALIYUN_OSS_ACCESSKEYID, ALIYUN_OSS_ACCESSKEYSECRET);

}

}

4、封装前端上传策略返回对象
前端直传时所需要的参数

    

package org.pp.oss.model;

/**

* TODO 获取OSS上传文件授权返回结果

*

* @author ss_419

* @version 1.0

* @date 2023/5/28 19:07

*/

public class OssPolicyResult {

private String accessKeyId;

// @ApiModelProperty("用户表单上传的策略,经过base64编码过的字符串") 13

private String policy;

// @ApiModelProperty("对policy签名后的字符串") 15

private String signature;

// @ApiModelProperty("上传文件夹路径前缀") 17

private String dir;

// @ApiModelProperty("oss对外服务的访问域名") 19

private String host;

// @ApiModelProperty("上传成功后的回调设置")

private String callback;

// 忽略getter、setter方法

}

5、封装上传成功回调参数对象
当OSS上传成功后,会根据该配置参数来回调对应接口

    

package org.pp.oss.model;

/**

* TODO oss上传成功后的回调参数

*

* @author ss_419

* @version 1.0

* @date 2023/5/28 19:10

*/

public class OssCallbackParam {

//请求的回调地址

private String callbackUrl;

//回调是传入request中的参数

private String callbackBody;

//回调时传入参数的格式,比如表单提交形式

private String callbackBodyType;

public String getCallbackUrl() {

return callbackUrl;

}

public void setCallbackUrl(String callbackUrl) {

this.callbackUrl = callbackUrl;

}

public String getCallbackBody() {

return callbackBody;

}

public void setCallbackBody(String callbackBody) {

this.callbackBody = callbackBody;

}

public String getCallbackBodyType() {

return callbackBodyType;

}

public void setCallbackBodyType(String callbackBodyType) {

this.callbackBodyType = callbackBodyType;

}

}

6、封装上传成功后回调结果对象
回调接口中返回的数据对象,封装了上传文件的信息

    

/**

* TODO oss上传文件的回调结果

*

* @author ss_419

* @version 1.0

* @date 2023/5/28 19:14

*/

public class OssCallbackResult {

private String filename;// 文件名称

private String size;// 文件大小

private String mimeType;// 文件的mimeType

private String width;// 图片文件的宽

private String height;// 图片文件的高

// 忽略getter、setter方法

}

7、添加OSS业务接口OssService

    

/**

* TODO oss上传管理Service

*

* @author ss_419

* @version 1.0

* @date 2023/5/28 19:16

*/

public interface OssService {

/**

* oss上传策略生成

* @return

*/

OssPolicyResult policy();

/**

* oss上传成功回调

* @param request

* @return

*/

OssCallbackResult callback(HttpServletRequest request);

}

8、OssService实现类

    

package org.pp.oss.service.impl;/*

package org.pp.oss.service.impl;

import org.pp.oss.model.OssCallbackResult;

import org.pp.oss.model.OssPolicyResult;

import org.pp.oss.service.OssService;

import javax.servlet.http.HttpServletRequest;

*/

import cn.hutool.json.JSONUtil;

import com.aliyun.oss.OSSClient;

import com.aliyun.oss.common.utils.BinaryUtil;

import com.aliyun.oss.model.MatchMode;

import com.aliyun.oss.model.PolicyConditions;

import org.pp.oss.model.OssCallbackParam;

import org.pp.oss.model.OssCallbackResult;

import org.pp.oss.model.OssPolicyResult;

import org.pp.oss.service.OssService;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;

import java.text.SimpleDateFormat;

import java.util.Date;

/**

* TODO

*

* @author ss_419

* @version 1.0

* @date 2023/5/28 19:17

*/

@Service

public class OssServiceImpl implements OssService {

private static final Logger LOGGER = LoggerFactory.getLogger(OssServiceImpl.class);

@Value("${aliyun.oss.policy.expire}")

private int ALIYUN_OSS_EXPIRE;

@Value("${aliyun.oss.maxSize}")

private int 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;

@Autowired

private OSSClient ossClient;

/**

* 签名生成

*

* @return

*/

@Override

public OssPolicyResult policy() {

OssPolicyResult result = new OssPolicyResult();

// 存储目录

SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");

String baseDir = ALIYUN_OSS_DIR_PREFIX + sdf.format(new Date());

// 签名有效期

long expireEndTime = System.currentTimeMillis() + ALIYUN_OSS_EXPIRE * 1000;

Date expiration = new Date(expireEndTime);

// 文件大小

long maxSize = ALIYUN_OSS_MAX_SIZE * 1024 *1024;

// 回调地址

OssCallbackParam callback = new OssCallbackParam();

callback.setCallbackUrl(ALIYUN_OSS_CALLBACK);

callback.setCallbackBody("filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");

callback.setCallbackBody("application/x-www-form-urlencoded");

// 提交节点

String action = "https://" + 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, baseDir);

String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);

byte[] binaryData = postPolicy.getBytes("utf-8");

String policy = BinaryUtil.toBase64String(binaryData);

String signature = ossClient.calculatePostSignature(postPolicy);

String callbackData = BinaryUtil.toBase64String(JSONUtil.parse(callback).toString().getBytes("UTF-8"));

// 返回结果

result.setAccessKeyId(ossClient.getCredentialsProvider().getCredentials().getAccessKeyId());

result.setPolicy(policy);

result.setSignature(signature);

result.setDir(baseDir);

result.setCallback(callbackData);

result.setHost(action);

} catch (Exception e) {

LOGGER.error("签名生成失败{e}", e);

}

return result;

}

@Override

public OssCallbackResult callback(HttpServletRequest request) {

OssCallbackResult result = new OssCallbackResult();

String filename = request.getParameter("filename");

filename = "http://".concat(ALIYUN_OSS_BUCKET_NAME).concat(".").concat(ALIYUN_OSS_ENDPOINT).concat("/").concat(filename);

result.setFilename(filename);

result.setSize(request.getParameter("size"));

result.setMimeType(request.getParameter("mimeType"));

result.setHeight(request.getParameter("height"));

result.setWidth(request.getParameter("width"));

return result;

}

}

9、定义OssController接口

    

package org.pp.oss.controller;

import cn.hutool.json.JSONObject;

import com.aliyun.oss.OSS;

import com.aliyun.oss.OSSClientBuilder;

import com.aliyun.oss.common.utils.BinaryUtil;

import com.aliyun.oss.model.MatchMode;

import com.aliyun.oss.model.PolicyConditions;

import org.pp.oss.model.OssCallbackResult;

import org.pp.oss.model.OssPolicyResult;

import org.pp.oss.service.OssService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.CrossOrigin;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.util.LinkedHashMap;

import java.util.Map;

/**

* TODO Oss相关操作接口

*

* @author ss_419

* @version 1.0

* @date 2023/5/28 20:43

*/

@RestController

@RequestMapping("/aliyun/oss")

@CrossOrigin

public class AliyunOssController {

@Autowired

private OssService ossService;

@CrossOrigin

@RequestMapping("/policys")

public Map<String,String> policysMap(){

// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。

String accessId = "xxx";

String accessKey = "xxx";

// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。

String endpoint = "oss-cn-shanghai.aliyuncs.com";

// 填写Bucket名称,例如examplebucket。

String bucket = "xxx";

// 填写Host地址,格式为https://bucketname.endpoint。

String host = "https://" + bucket + "."+ endpoint;

// 设置上传回调URL,即回调服务器地址,用于处理应用服务器与OSS之间的通信。OSS会在文件上传完成后,把文件上传信息通过此回调URL发送给应用服务器。

// String callbackUrl = "https://192.168.0.0:8888";

// 设置上传到OSS文件的前缀,可置空此项。置空后,文件将上传至Bucket的根目录下。

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

String formatData = dateFormat.format(new Date());

String dir = "osstest/"+formatData+ "/";

// 创建ossClient实例。

OSS ossClient = new OSSClientBuilder().build(endpoint, accessId, accessKey);

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);

Map<String, String> 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));

return respMap;

// respMap.put("expire", formatISO8601Date(expiration));

// 回调数据

// JSONObject jasonCallback = new JSONObject();

// jasonCallback.put("callbackUrl", callbackUrl);

// jasonCallback.put("callbackBody",

// "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");

// jasonCallback.put("callbackBodyType", "application/x-www-form-urlencoded");

// String base64CallbackBody = BinaryUtil.toBase64String(jasonCallback.toString().getBytes());

// respMap.put("callback", base64CallbackBody);

//

// JSONObject ja1 = JSONObject.fromObject(respMap);

// // System.out.println(ja1.toString());

// response.setHeader("Access-Control-Allow-Origin", "*");

// response.setHeader("Access-Control-Allow-Methods", "GET, POST");

// response(request, response, ja1.toString());

} catch (Exception e) {

// Assert.fail(e.getMessage());

System.out.println(e.getMessage());

}

return null;

}

/**

* oss上传签名生成

* @return

*/

@CrossOrigin

@RequestMapping("/policy")

public OssPolicyResult policy(){

OssPolicyResult result = ossService.policy();

System.out.println("result = " + result);

return result;

}

/**

* oss上传成功回调

* @return

*/

@RequestMapping("/callback")

public OssCallbackResult callback(HttpServletRequest request){

OssCallbackResult callback = ossService.callback(request);

System.out.println("callback = " + callback);

return callback;

}

}

对接口进行测试,如下图所示,请求返回了oss文件上传时所需的对应参数

4、Vue文件上传测试代码

这里为了更加方便快捷的进行文件上传接口的测试,我选择使用Vue+Element-Ui来搭建一个简单的上传案例
1、创建Vue项目
在控制台中输入vue ui,启动vue项目图形管理界面

访问http://localhost:8000 ,进入如下图操作界面即代表启动成功

找到项目管理器,创建一个新Vue项目

 

这里选择Vue2版本



创建成功后添加本次案例所需要的依赖:

  • axios:用于对后端服务发起Ajax请求

  • element-ui:本案例使用到该ui组件库中的Upload,用于文件上传
    在Vue项目中的main.js中启用对应依赖

    

import Vue from 'vue'

import App from './App.vue'

import ElementUI from 'element-ui';

import 'element-ui/lib/theme-chalk/index.css';

import axios from "axios";

import VueAxios from "vue-axios";

import router from './router'

import store from './store'

Vue.config.productionTip = false

// Vue.use(axios)

Vue.use(VueAxios,axios)

Vue.use(ElementUI);

new Vue({

router,

store,

render: function (h) { return h(App) }

}).$mount('#app')

创建OssUpload组件,该组件可以在项目中引用

    

<template>

<el-upload

class="upload-demo"

:action="objData.host"

:before-upload="ossPolicy"

:data="objData"

:file-list="fileList"

list-type="picture">

<el-button size="small" type="primary">点击上传</el-button>

<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>

</el-upload>

</template>

<script>

export default {

data() {

return {

fileList: [],

objData: {

OSSAccessKeyId: '',

policy: '',

signature: '',

key: '',

host: '',

dir: ''

}

};

},

methods: {

ossPolicy(file) {

let _self = this;

// 在上传前 进行服务器签名

return new Promise((resolve, reject) => {

this.axios.get("http://localhost:8080/aliyun/oss/policy")

.then(response => {

console.log(response)

_self.objData.OSSAccessKeyId = response.data.accessKeyId

_self.objData.policy = response.data.policy

_self.objData.signature = response.data.signature

_self.objData.dir = response.data.dir

_self.objData.host = response.data.host+''

_self.objData.key = response.data.dir + "${filename}"

resolve(true) // 继续上传

})

.catch(error => {

console.log(error)

reject(false)

})

}

)

}

}

}

</script>

<style>

</style>

在HelloWorld.vue中引用文件上传组件

```js

<template>

<div class="hello">

<h1>{{ msg }}</h1>

<OssUpload></OssUpload>

</div>

</template>

<script>

// 引用组件

import OssUpload from "@/components/OssUpload.vue";

export default {

name: 'HelloWorld',

components: {OssUpload},

props: {

msg: String

}

}

</script>

<style >

</style>

前后端联调

  1. 启动后端服务

  2. 启动前端项目

选择文件进行上传,如下图所示即表示上传成功

查看对应的OSSBucket,图片已成功存储至OSS服务中

给大家推荐我们团队开发的Chrome插件:YouTube中文配音(https://www.youtube-dubbing.com/)。如果您跟我们一样,热爱看国外的视频学习前沿知识或者其他内容,该插件可以很好的帮助您讲外语视频一键转化为中文视频!




END



Spring Boot 实现限流注解
Java 21 分代 ZGC 和虚拟线程上手体验
Spring Boot虚拟线程的性能还不如Webflux?
Spring官方出品的状态机框架,简单又优雅!

高质量交流群,关注:SpringForAll,回复关键词:加群

相关推荐

  • 面试居然碰到了前女友,真的人都麻了
  • hippo - 提升我们团队工程效率的工具
  • 2024年,Rust 与 JavaScript 的较量!谁将成为编程领域的新王者?
  • 阿里家属:程序员老公10年薪资变化,从12年月薪三千到现在年入百万以上
  • 纯CSS实现跑马灯效果,CSS动画知识是该补一补了~
  • 开源 2 年、打磨 13 年、300 万行代码的开源项目
  • 分享一位27岁的前端,从二本到澳洲🦘的故事
  • 分享7个有用的Node.js库,让你事半功倍
  • 卧槽,又来一个 Java 神器!!
  • 计算,为了无法计算的价值
  • 拜登政府发布AI行政命令;国内超一半大模型公司跑在阿里云上;ChatGPT被曝参数规模为200亿丨AIGC大事日报
  • 苹果亮出全球首个3nm PC芯片!920亿晶体管,功耗直接砍半?MBP换芯不换面
  • 通义千问2.0来了!实测编程打败8成Python用户,阿里云大模型「全家桶」炸场
  • [开源]一款终端仿真软件,支持多种后端协议,无依赖跨平台使用
  • 10种顶流聚类算法Python实现
  • 如何设计一个安全好用的OpenApi
  • 究竟是用什么工具让郭德纲讲起了英文相声,马斯克做中文演讲?
  • 1031.AI日报:关于AI可能对人类未来构成威胁,有人挺开放性监管,有人警告垄断
  • 最容易出错的 Hive Sql 详解
  • 事件图谱构建如何进行事件标注:Duee等代表性事件标注数据集解析与Marktool事件标注动手实现