文章目录
- 1.1.1 流程和源码解析
- 1.1.2 实现步骤
- 1.2 Aliyun Spring Boot OSS 示例(接入微服务)
- 1.2.1 简略说明
- 1.2.2步骤
- 2.1 后端设置
- 2.2 前端设置
- 2.3 实现的效果
前言
以下只提供一种思路,对新手可能不太友好。
这里将前端Vue上传的图片直接存储到服务器上,
Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
1 前提知识
1.1 服务端签名后直传
官网地址文档:服务端签名后直传
介绍如何基于Post Policy的使用规则在服务端通过各种语言代码完成签名,然后通过表单直传数据到OSS。由于服务端签名直传无需将AccessKey暴露在前端页面,相比JavaScript客户端签名直传具有更高的安全性。
1.1.1 流程和源码解析
1.1.2 实现步骤
- 1.用户向应用服务器请求上传Policy和回调。
将客户端源码中的upload.js文件的以下代码片段的变量serverUrl的值设置为应用服务器的URL。
// serverUrl是用户获取签名和Policy等信息的应用服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
serverUrl = 'http://88.88.XX.XX:8888'
设置完成后,客户端会向该serverUrl发送Get请求来获取需要的信息。
本场景为服务端签名后直传,不涉及上传回调。因此,您需要注释客户端源码中的upload.js文件内的’callback’ : callbackbody字段,以关闭上传回调功能。
{
'key' : key + '${filename}',
'policy': policyBase64,
'OSSAccessKeyId': accessid,
// 设置服务端返回200状态码,默认返回204。
'success_action_status' : '200',
'callback' : callbackbody,
'signature': signature,
}
- 应用服务器返回上传Policy和签名给用户。应用服务器侧的签名直传服务会处理客户端发送的Get请求消息,您可以设置对应的代码让应用服务器能够给客户端返回正确的消息。以下是签名直传服务返回给客户端消息Body内容的示例:
- 3.用户使用Post方法向OSS发送文件上传请求`
> 说明
除file表单域外,包含key在内的其他所有表单域的大小均不能超过8 KB。
客户端上传默认同名覆盖,如果您不希望覆盖同名文件,可以在上传请求的header中携带参数x-oss-forbid-overwrite,并指定其值为true。当您上传的文件在OSS中存在同名文件时,该文件会上传失败,并返回FileAlreadyExists错误。
```java
new_multipart_params = {
// key表示上传到Bucket内的Object的完整路径,例如exampledir/exampleobject.txtObject,完整路径中不能包含Bucket名称。
// filename表示待上传的本地文件名称。
'key' : key + '${filename}',
'policy': policyBase64,
'OSSAccessKeyId': accessid,
// 设置服务端返回状态码为200,不设置则默认返回状态码204。
'success_action_status' : '200',
'signature': signature,
};
1.2 Aliyun Spring Boot OSS 示例(接入微服务)
1.2.1 简略说明
项目说明:
如果您的应用是 Spring Cloud 应用,且需要使用阿里云的 OSS 服务进行云端的文件存储,例如电商业务中常见的商品图片存储,那么您可以使用 OSS starter 完成 Spring Cloud 应用的对象存储。
1.2.2步骤
简略步骤,更加详细说明请自行查看官网说明
1 修改 pom.xml 文件,引入 aliyun-oss-spring-boot-starter。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>aliyun-oss-spring-boot-starter</artifactId>
</dependency>
- 2、在配置文件中配置 OSS 服务对应的 accessKey、secretKey 和 endpoint。
// application.properties
alibaba.cloud.access-key=your-ak
alibaba.cloud.secret-key=your-sk
alibaba.cloud.oss.endpoint=***
- 3、注入 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"));
}
}
2、项目中实际应用
2.1 后端设置
在父工程下,新创建一个服务专门用来处理其它第三方的服务。
pom文件中配置
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-oss</artifactId>
</dependency>
application.yml配置文件修改
同时需要将服务注册到nacos中
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
alicloud:
access-key: +++
secret-key: +++
oss:
endpoint: +++
bucket: ++++
application:
name: 名称
server:
port: 90000
nacos配置动态修改
spring.application.name=+++
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.namespace=++++
spring.cloud.nacos.config.ext-config[0].data-id=oss.yml
spring.cloud.nacos.config.ext-config[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.ext-config[0].refresh=true
编写一个接口,用来实现服务器将上传Policy和签名返回给用户
@RestController
public class OssController {
@Autowired
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 R policy() {
String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint
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));
} catch (Exception e) {
System.out.println(e.getMessage());
}
return R.ok().put("data",respMap);
}
}
2.2 前端设置
上传封装为一个组件
<template>
<div>
<el-upload
action="服务器要存储的地址"
: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 { getUUID } from '@/utils'
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)
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>
上传成功后的图片回显
<el-table-column
prop="logo"
header-align="center"
align="center"
label="品牌logo地址"
>
<template slot-scope="scope">
<img :src="scope.row.logo" style="width: 100px; height: 80px" />
</template>
</el-table-column>
2.3 实现的效果
图片上传
添加成功后,图片展示
服务器上的的图片