前置:
自行开通阿里oss服务:
https://www.aliyun.com/product/oss?spm=a2c4g.11174283.J_8058803260.125.d9387da2TjNfP6
oss API:
https://help.aliyun.com/document_detail/32013.html?spm=a2c4g.11186623.6.956.65694697WhZebu
一、简单的文件上传:
依赖:
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
测试代码:
@Test
public void testOssUPLoad() throws Exception{
// Endpoint以杭州为例,其它Region请按实际情况填写。
String endpoint = "https://oss-cn-guangzhou.aliyuncs.com";
// 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。
// 强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录RAM控制台创建RAM账号。
//LTAI4GKxxxxxekn2ZYLxab
//9XnhRAnlxxxxxxTDMLpMxGLoQx6r
String accessKeyId = "LTAI4GKxxxxxekn2ZYLxab";
String accessKeySecret = "9XnhRAnlxxxxxxTDMLpMxGLoQx6r";
String bucketName = "xxxx-oos-images";
// <yourObjectName>上传文件到OSS时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg。
String objectName = "C:\\Users\\Administrator\\Downloads\\6555c4f8ffa80914bc51b798e4a6dce6.jpeg";
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
//以每天一个文件夹保存上传的文件(文件名称自行优化)
String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
String content = format+"/Hello OSS.jpeg";
// 上传文件到指定的存储空间(bucketName)并将其保存为指定的文件名称(objectName)。
ossClient.putObject(bucketName, content, new FileInputStream(objectName));
// 关闭OSSClient。
ossClient.shutdown();
}
二、使用spring-cloud-starter-alicloud-oss对文件直传进行改善
2.1、starter用2.2.0RELEASE 如果使用依赖管理的默认2.2.3RELEASE则无法启动
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-oss</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
如果上边的starter依赖有冲突(启动失败)可以使用下边的依赖替换上边的依赖试试:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>aliyun-oss-spring-boot-starter</artifactId>
<version>1.0.0</version>
<exclusions>
<exclusion>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-oss</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.5.7</version>
</dependency>
补充:项目使用的版本如下:(没有版本问题可不看)
//1.spring boot 版本
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
//2、spring cloud 以及spring cloud alibaba 版本,版本可以在properties里标注也直接在
//dependencyManagement 表明
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.2、配置OSS
spring:
application:
name: xxxx
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
alicloud:
access-key: LTAI4GKzxxxxekn2ZYLxab
secret-key: 9XnhRAxxxxxxxxxxxxxxxxxxLpMxGLoQx6r
oss:
endpoint: xxxxxxxxxxxxxxxxxxxxxxxx
server:
port: 30000
2.3、测试
//注入OSSClient
@Autowired
OSSClient ossClient;
@Test
void contextLoads() throws Exception {
String objectName = "C:\\Users\\Administrator\\Downloads\\6555c4f8ffa80914bc51b798e4a6dce6.jpeg";
String bucketName = "xxx-oss-images";
String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
String content = format+"/ss335OSS.jpeg";
ossClient.putObject(bucketName, content, new FileInputStream(objectName));
// 关闭OSSClient。
ossClient.shutdown();
}
}
三、服务端签名后直传
阿里文档:
https://help.aliyun.com/document_detail/31926.html?spm=a2c4g.11186623.6.1738.442534f3qJ9Nb7
1、依赖(依赖版本同上 二)
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-oss</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
2、配置
spring:
cloud:
alicloud:
access-key: LTAI4xxxxxxxZYLxab
secret-key: 9XnxxxxxxxxxxxxxxxxxxxxxGLoQx6r
oss:
endpoint: xxxxxxxxxxxxxxxxxxxxxx
## 自定义添加的属性
bucket: mybucket
2、代码
@RestController
@RequestMapping("thirdparty/oss")
public class OSSController {
@Autowired
OSS ossClient;//这里不能写OSSClient,因为容器总注入的是OSS,而不是其OSSClient实现,可以参看OssContextAutoConfiguration
@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 ossUpload() {
// host的格式为 bucketname.endpoint
String host = "https://" + bucket + "." + 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);
// 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);//阿里oss服务提供的签名
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);
}
}
}
到这里可以使用idea自带的REST client 先测试后端是否可以正常获取到签名。
获取到的信息如下:
3、前端代码或者参考阿里提供的原生客户端:(https://docs-aliyun.cn-hangzhou.oss.aliyun-inc.com/assets/attach/86983/APP_zh/1537971352825/aliyun-oss-appserver-js-master.zip?spm=a2c4g.11186623.2.10.74c47403C0rJnK&file=aliyun-oss-appserver-js-master.zip)
3.1异步获取签名
src/components/upload/policy.js文件如下:这里对异步方法axios进行了封装(自行使用原生axios或者ajax进行替换即可)
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);
})
});
}
3.2vue+element组件提供的上传组件
src/components/upload/singleUpload.vue文件如下
<template>
<div>
<el-upload
action="https://xxxxxx这里填 阿里oss提供 Bucket域名"
:data="dataObj"
list-type="picture"
:multiple="false"
:show-file-list="showFileList"
:file-list="fileList"
:before-upload="beforeUpload"
:on-remove="handleRemove"
:on-success="handleUploadSuccess"
:on-error="handleUploadFail"
:on-preview="handlePreview">
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过10MB</div>
</el-upload>
<!-- file-list上传的文件列表, 例如: [{name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg'}] -->
<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'//uuid,随便自定义即可
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: {
handleUploadFail(){
console.log("文件上传失败!!");
},
emitInput(val) {
//this.$emit('input', val) 的意思是出发input事件(自定义事件也可以),val为input事件的参数
this.$emit('input', val)
},
handleRemove(file, fileList) {
this.emitInput('');
},
handlePreview(file) {
this.dialogVisible = true;
},
//上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。
beforeUpload(file) {
console.log(`文件详情:`,file);
let _self = this;
return new Promise((resolve, reject) => {
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.key = response.data.dir + getUUID()+file.name;//这样也是可以的
_self.dataObj.dir = response.data.dir;
_self.dataObj.host = response.data.host;
resolve(true)
}).catch(err => {
reject(false)
})
})
},
handleUploadSuccess(res, file) {
//此代码用于文件上传完毕后,返回图片地址给
//<single-upload v-model="dataForm.logo"></single-upload>
console.log("上传成功...")
this.showFileList = true;
this.fileList.pop();//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>
3.3 测试组件test.vue
<template>
<!-- 使用单文件上传组件 -->
<single-upload v-model="image"></single-upload>
</template>
<script>
import SingleUpload from "@/components/upload/singleUpload";
export default {
data() {
return {};
},
components: {
SingleUpload,
},
}
</script>