- 创建阿里云OSS对象存储服务
- 创建bucket存储空间
- 创建RAM子用户Accesskey进行API调用(安全性考虑)
- 添加权限
- 创建跨域规则解决跨域问题
- 代码有关操作
-
引入阿里云oss依赖
<!-- 使用阿里云的oss对象存储服务--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alicloud-oss</artifactId> </dependency>
-
在yml中配置oss相关信息
spring: cloud: alicloud: # RAM子用户账号 access-key: # RAM子用户密码 secret-key: oss: # 地址节点 endpoint:
-
在应用服务器使用OSSClient进行相关操作(应用服务器负载太大)
@SpringBootTest @Slf4j class GulimallProductApplicationTests { @Autowired private BrandService brandService; @Autowired OSSClient ossClient; @Test void testUpload() throws FileNotFoundException { // 上传文件流。 InputStream inputStream = new FileInputStream("/Users/zhaolijian/Projects/springCloud_learning/miaosha/html/static/assets/global/plugins/fancybox/demo/3_b.jpg"); ossClient.putObject("gulimall-zlj", "3_b.jpg", inputStream); // 关闭OSSClient。 ossClient.shutdown(); System.out.println("上传完成"); }
- 使用签名(policy策略)方式上传
-
使用签名上传策略原因
采用JavaScript客户端直接签名时,AccessKey ID和AcessKey Secret会暴露在前端页面,因此存在严重的安全隐患。因此,OSS提供了服务端签名后直传的方案。
本示例中,Web端向服务端请求签名,然后直接上传,不会对服务端产生压力,而且安全可靠。但本示例中的服务端无法实时了解用户上传了多少文件,上传了什么文件。如果想实时了解用户上传了什么文件,可以采用服务端签名直传并设置上传回调。 -
后端代码: 获取签名
package com.kenai.gulimall.thirdparty.controller; import com.aliyun.oss.OSS; 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.kenai.common.utils.R; 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 java.text.SimpleDateFormat; import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; @RestController @RequestMapping("oss") public class OssController { @Autowired private 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("/policy") public R policy(){ 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 = 30; long expireEndTime = System.currentTimeMillis() + expireTime * 1000; Date expiration = new Date(expireEndTime); PolicyConditions policyConds = new PolicyConditions(); //允许上传的文件大小限制: CONTENT_LENGTH_RANGE为5*1024*1024*1024=5G policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000); // 上传目录 policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir); //给policyConds添加过期时间并json序列化(格式iso8601:"yyyy-MM-dd'T'HH:mm:ss.fff'Z'") String postPolicy = ossClient.generatePostPolicy(expiration, policyConds); byte[] binaryData = postPolicy.getBytes("utf-8"); // 将policy的json字符串进行base64编码 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(); } return R.ok().put("data", respMap); } }
-
policy.js(前端请求签名代码)
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) }) }) }
-
singleUpload.vue(前端单文件上传代码)
<template> <div> <!-- list-type: 文件列表的类型 --> <!-- multiple: 是否支持多选文件 --> <!-- show-file-list: 是否显示已上传文件列表 --> <!-- file-list: 上传的文件列表 --> <!-- before-upload: 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。 --> <!-- on-remove: 文件列表移除文件时的钩子 --> <!-- on-success: 文件上传成功时的钩子 --> <!-- on-preview: 点击文件列表中已上传的文件时的钩子 --> <el-upload action="http://gulimall-zlj.oss-cn-hangzhou.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文件,且不超过5MB</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 { getUUID } from '@/utils' export default { name: 'singleUpload', props: { value: String }, computed: { imageUrl () { return this.value }, imageName () { if (this.value != null && this.value !== '') { // 截取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 }, // 在上传之前向应用服务器请求policy签名 beforeUpload (file) { let _self = this return new Promise((resolve, reject) => { // response为Promise异步请求调用成功后resolve回调函数中的data policy() .then(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 console.log('响应的数据', _self.dataObj) resolve(true) }) .catch(err => { console.log('错误信息', err) reject(new Error(false)) }) }) }, handleUploadSuccess (res, file) { 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>
-
multiUpload.vue(前端多文件上传代码)
<template> <div> <el-upload action="http://gulimall-zlj.oss-cn-hangzhou.aliyuncs.com" :data="dataObj" :list-type="listType" :file-list="fileList" :before-upload="beforeUpload" :on-remove="handleRemove" :on-success="handleUploadSuccess" :on-preview="handlePreview" :limit="maxCount" :on-exceed="handleExceed" :show-file-list="showFile" > <i class="el-icon-plus"></i> </el-upload> <el-dialog :visible.sync="dialogVisible"> <img width="100%" :src="dialogImageUrl" alt /> </el-dialog> </div> </template> <script> import { policy } from "./policy"; import { getUUID } from '@/utils' export default { name: "multiUpload", props: { //图片属性数组 value: Array, //最大上传图片数量 maxCount: { type: Number, default: 30 }, listType:{ type: String, default: "picture-card" }, showFile:{ type: Boolean, default: true } }, data() { return { dataObj: { policy: "", signature: "", key: "", ossaccessKeyId: "", dir: "", host: "", uuid: "" }, dialogVisible: false, dialogImageUrl: null }; }, // computed用来监控自己定义的变量fileList, 当this.value发生变化时,fileList也发生变化 computed: { fileList() { let fileList = []; for (let i = 0; i < this.value.length; i++) { fileList.push({ url: this.value[i] }); } return fileList; } }, mounted() { }, methods: { emitInput(fileList) { let value = []; for (let i = 0; i < fileList.length; i++) { value.push(fileList[i].url); } this.$emit("input", value); }, handleRemove(file, fileList) { this.emitInput(fileList); }, handlePreview(file) { this.dialogVisible = true; this.dialogImageUrl = file.url; }, beforeUpload(file) { let _self = this; return new Promise((resolve, reject) => { policy() .then(response => { // console.log("这是什么${filename}"); _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 => { console.log("出错了...",err) reject(false); }); }); }, handleUploadSuccess(res, file) { this.fileList.push({ name: file.name, // url: this.dataObj.host + "/" + this.dataObj.dir + "/" + file.name; 替换${filename}为真正的文件名 url: this.dataObj.host + "/" + this.dataObj.key.replace("${filename}",file.name) }); this.emitInput(this.fileList); }, handleExceed(files, fileList) { this.$message({ message: "最多只能上传" + this.maxCount + "张图片", type: "warning", duration: 1000 }); } } }; </script> <style> </style>