SpringCloud阿里云实现OSS
SpringCloud阿里云实现OSS
在启动示例进行演示之前,我们先了解一下如何接入 OSS。
接入相关操作只需修改 accessKey、secretKey、endpoint 即可。
修改 pom.xml 文件,引入 aliyun-oss-spring-boot-starter。
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>aliyun-oss-spring-boot-starter</artifactId> </dependency>
SpringCloud整合的start为下面的
<-- 应该为下面的--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alicloud-oss</artifactId> <version>2.2.0.RELEASE</version> </dependency>
在配置文件中配置 OSS 服务对应的 accessKey、secretKey 和 endpoint。
// application.properties alibaba.cloud.access-key=your-ak
alibaba.cloud.secret-key=your-sk
alibaba.cloud.oss.endpoint=***
以阿里云 accessKey、secretKey 为例,获取方式如下。
i. 在阿里云控制台界面,单击右上角头像,选择 accesskeys,或者直接登录用户信息管理界面:获取相关key信息填入
使用SpringCloud阿里云封装好的OSSClient直接进行上传
测试用例 @SpringBootTest public class TestFor { @Resource OSSClient ossClient; @Test public void te() throws FileNotFoundException { FileInputStream inputStream = new FileInputStream("C:\\Users\\Pictures\\903d28ab0e2f4bb59bdf8f7c6fd22fe4.jpg"); ossClient.putObject("gulimall-xuda","903d28ab0e2f4bb59bdf8f7c6fd22fe4.jpg",inputStream); ossClient.shutdown(); System.out.println("上传完成"); } }
注意:如果您使用了阿里云 STS服务 进行短期访问权限管理,则除了 accessKey、secretKey、endpoint 以外,还需配置 securityToken。
注入 OSSClient 并进行文件下载操作。
@Service public class YourService { @Autowired private OSSClient ossClient; public void saveFile() { // download file to local ossClient.getObject(new GetObjectRequest(bucketName, objectName), new File("pathOfYourLocalFile")); } }
说明:直接注入OSSClient 方式通常用于大量文件对象操作的场景。如果仅仅是需要读取文件对象内容,OSS Starter 也支持以 Resource 方式读取文件, 前后端整合进行阿里云OSS
上传流程阿里云对象存储-服务端签名后直传
通常我们是前端将图片数据上传到后端再通过后端上传到OSS的,但这样直接使用服务器进行交互会使我们的服务器压力很大,
因为上传到OSS需要带上key的账户和密码,所以我们可以向服务器上传我们的请求,服务器将OSSkey生成一个签名数据返回给前端,前端将图片与签名数据进行携带直接与阿里云OSS进行交互上传,这样可以更快更节省服务器压力 可以参考下列文档 阿里云OSS服务端签名直传地址 在这里插入图片描述 网关设置
使用到nacos作为服务注册,gateway网关作为服务分发
spring: cloud: nacos: discovery: server-addr: 127.0.0.1:8848 gateway: routes: - id: test_route uri: https://www.baidu.com predicates: - Query=url,baidu - id: third_party_route uri: lb://gulimall-third-party predicates: - Path=/api/thirdparty/** filters: - RewritePath=/api/thirdparty/(?<segment>.*),/$\{segment} application: name: gulimall-gateway server: port: 89
网关配置跨域请求权限
package com.xuda.gateway.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; /** * @author :程序员徐大大 * @description:统一配置跨域请求 * @date :2022-07-19 22:21 */ @Configuration public class CoresConfigur { @Bean public CorsWebFilter corsWebFilter() { UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); CorsConfiguration coresConfiguration = new CorsConfiguration(); //配置跨域 coresConfiguration.addAllowedHeader("*"); coresConfiguration.addAllowedMethod("*"); // coresConfiguration.addAllowedOrigin("*"); //allowedOriginPatterns coresConfiguration.addAllowedOriginPattern("*"); coresConfiguration.setAllowCredentials(true); urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",coresConfiguration); return new CorsWebFilter(urlBasedCorsConfigurationSource); } }
创建第三方OSS项目
创建yml相关配置
spring: cloud: alicloud: access-key: your-key secret-key: your-key oss: endpoint: oss-cn-shanghai.aliyuncs.com nacos: discovery: server-addr: 127.0.0.1:8848 application: name: third-party server: port: 8900
编写回调OSS签名密钥代码
package com.xuda.thirdparty.controller; import com.alibaba.fastjson.JSONObject; 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 com.xuda.common.utils.R; 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 :程序员徐大大 * @description:TODO * @date :2022-07-21 21:35 */ @RestController public class OssController { @Resource private OSSClient ossClient; @Value("${spring.cloud.alicloud.oss.endpoint}") private String endpoint; @Value("${spring.cloud.alicloud.access-key}") private String accessId; @RequestMapping("/oss/policy") public R policy() { // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。 // 填写Bucket名称,例如examplebucket。 String bucket = "gulimall-xuda"; // 填写Host地址,格式为https://bucketname.endpoint。 String host = "https://" + bucket + "."+ endpoint; // 设置上传回调URL,即回调服务器地址,用于处理应用服务器与OSS之间的通信。OSS会在文件上传完成后,把文件上传信息通过此回调URL发送给应用服务器。 // String callbackUrl = "https://192.168.0.0:8888"; // 设置上传到OSS文件的前缀,可置空此项。置空后,文件将上传至Bucket的根目录下。 //将日期作为上传的文件夹目录 String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); String dir = format+"/"; 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", 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()); } return R.ok().put("data",respMap); //创建返回JSON数据封装 } }
JSON返回代码
package com.xuda.common.utils; import org.apache.http.HttpStatus; import java.util.HashMap; import java.util.Map; /** * 返回数据 * */ public class R extends HashMap<String, Object> { private static final long serialVersionUID = 1L; public R() { put("code", 0); put("msg", "success"); } public static R error() { return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知异常,请联系管理员"); } public static R error(String msg) { return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg); } public static R error(int code, String msg) { R r = new R(); r.put("code", code); r.put("msg", msg); return r; } public static R ok(String msg) { R r = new R(); r.put("msg", msg); return r; } public static R ok(Map<String, Object> map) { R r = new R(); r.putAll(map); return r; } public static R ok() { return new R(); } public R put(String key, Object value) { super.put(key, value); return this; } }
前端VUE代码示例
<template> <el-dialog :title="!dataForm.brandId ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible" > <el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="140px" > <el-form-item label="品牌名" prop="name"> <el-input v-model="dataForm.name" placeholder="品牌名"></el-input> </el-form-item> <el-form-item label="品牌logo地址" prop="logo"> <single-upload v-model="dataForm.logo"></single-upload> </el-form-item> </el-form> <span slot="footer" class="dialog-footer"> <el-button @click="visible = false">取消</el-button> <el-button type="primary" @click="dataFormSubmit()">确定</el-button> </span> </el-dialog> </template> <script> import SingleUpload from "@/components/upload/singleUpload"; export default { components: { SingleUpload }, data() { return { visible: false, dataForm: { brandId: 0, name: "", logo: "", }, dataRule: { name: [{ required: true, message: "品牌名不能为空", trigger: "blur" }], logo: [ { required: true, message: "品牌logo地址不能为空", trigger: "blur" }, ], }, }; }, methods: { init(id) { this.dataForm.brandId = id || 0; this.visible = true; this.$nextTick(() => { this.$refs["dataForm"].resetFields(); if (this.dataForm.brandId) { this.$http({ url: this.$http.adornUrl( `/product/brand/info/${this.dataForm.brandId}` ), method: "get", params: this.$http.adornParams(), }).then(({ data }) => { if (data && data.code === 0) { this.dataForm.name = data.brand.name; this.dataForm.logo = data.brand.logo; } }); } }); }, // 表单提交 dataFormSubmit() { this.$refs["dataForm"].validate((valid) => { if (valid) { this.$http({ url: this.$http.adornUrl( `/product/brand/${!this.dataForm.brandId ? "save" : "update"}` ), method: "post", data: this.$http.adornData({ brandId: this.dataForm.brandId || undefined, name: this.dataForm.name, logo: this.dataForm.logo, }), }).then(({ data }) => { if (data && data.code === 0) { this.$message({ message: "操作成功", type: "success", duration: 1500, onClose: () => { this.visible = false; this.$emit("refreshDataList"); }, }); } else { this.$message.error(data.msg); } }); } }); }, }, }; </script>
<template> <div> <el-upload action="http://gulimall-xuda.oss-cn-shanghai.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> </template> <script> import {policy} from './policy' //导入封装的请求地址 ,下面的可以直接加上 /** import http from '@/utils/httpRequest.js' export function policy() { return new Promise((resolve,reject)=>{ http({ url: http.adornUrl("/thirdparty/oss/policy"), method: "get", params: http.adornParams({}) }).then(({ data }) => { resolve(data); }) }); } */ import { getUUID } from '@/utils' //为了使用工具导入UUID /** //获取uuid,需要的可以加上进行更改 export function getUUID () { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { return (c === 'x' ? (Math.random() * 16 | 0) : ('r&0x3' | '0x8')).toString(16) }) } */ export default { name: 'singleUpload', 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) => { 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 +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); } } } </script> <style> </style>