又到了愉快的水文时间了,那么今天的带来的是如何使用第三放服务去愉快完成文件的上传服务。问题来了,为啥我们需要去使用第三方的文件存储服务咧,貌似是没有这个需求哇。其实呢这个咋说呢,确实是需要结合具体的业务情况来说的。一般情况下,如果我们只是做一个很简单的xx系统,例如学生管理系统这样的玩意,如果是这样的玩意的话,那么我们直接去上传到我们的服务器也是可以的,一般情况下最简单的方案就是:
当然也有最直接的就是直接把文件也存到数据库里面,但是对于数据库来说并不是很友好,当然这个也是一种解决方案。
但是这里有一个问题啊,那就是来一个文件我们就进行存储,先不讨论个别朋友喜欢XSS。咱们就说如果用户上传的内容,例如用户上传一张图片作为头像,然后咱们直接保存,之后展示出来别的用户也可以看到那么,如果用户上传的图片不符合社会主义核心价值观怎么处理,那么这样一来我们就需要做一个图片过滤,那么这样一来,一方面是服务器成本的上升,另一方面是技术问题,我们可能需要训练一个CNN 或者ViTranformer模型用户文件过滤而且这个还只是针对图片的,实际上我们还有文本什么的,那么有需要设计出一套方案,之后再结合文件存储技术,这样做基本上是比较困难的、而且不是dome级别的设计。 所以这个时候我们就需要使用到第三方的存储服务了。
此外还有一个问题就是,假设我们什么都不考虑,包括什么文件过滤,只要把文件存好就行,例如开发一个某sex网站,那么一样的随着流量的增大,考虑分布式架构的时候,服务当中的文件数据并不是互通的,因此也需要一个专门的文件存储服务来做这些事情,然后又回到了刚刚的问题,只是可能不需要文件审核服务。
所以当我们需要做一个网站并且需要允许并展示用户上传的内容时,我们就需要用到功能更加强大的文件存储服务,并且这个服务我们可以选择自己搭建,但是前期的成本太高,而且不符合“懒人“设定,因此我们就需要使用到第三放服务来玩玩了。
那么今天的博文就是使用第三方的文件存储服务进行操作,这里是以图片的存储为例子。并且完成,图片的过滤,也就是安全内容审核。
这里由于咱们都是使用SpringCloud Alibaba 所以的话,这边咱们的第三方组件也是使用阿里云的服务,因为有直接的支持嘛,那么我们这边就是使它的OSS,对象存储服务。
首先就是导入咱们的依赖,我这里是按照Alibaba的文档来的,但是它的文档是老版本的,也就是SpringCloud Alibaba 的版本还是2.1.x 的时候的,所以为了方便我这里还是返回了2.1.0 release 版本。
那么如果你还是2.2.6的话,那么如果你要导入的话,那么就这样导入:
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>aliyun-oss-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>com.aliyungroupId>
<artifactId>aliyun-java-sdk-coreartifactId>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.2.6.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>aliyun-spring-boot-dependenciesartifactId>
<version>1.0.0version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
当然如果你是按照了我的博文来的搭建的环境的话,直接像官方文档一样,先把SpringCloud Alibaba 的版本改为2.1.0 release
然后再像官方文档一样:
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alicloud-ossartifactId>
dependency>
进入阿里云,找到OSS服务,然后开通一下,之后的话我们可以见到这个:
之后的话,先创建一个bucket,这里的话注意设置为公开的权限就好了。
之后的话,你就可以看到你的这个玩意了,这个玩意相当于分组:
之后是赋予权限,这个就看你自己的了。
我这里是这个:
之后:
由于之后我们需要让前端上传文件,所以的话我们需要去配置跨越:
如果不配的话,就会这样:
之后我们可以在控制台看到文件上传成功:
那么到这里的话我们的服务是开通完了。
但是我们还需要有信息过滤。
之后就是俺们的第二个内容,我们找到内容安全服务,然后开通之后到这个页面:
绑定好我们的OSS。然后配置服务。
我们去创建增量或者是存量扫描,你只需要去填写配置就好了。
之后我们去测试一下:
并且回到OSS存储中,你可以发现两个文件的属性已经发生了改变。
到此我们的服务是开通完了。
我们使用授权访问的策略
这个文档里面也有,我这边直接给代码了:
/**
* 对象存储
*/
@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;
/**
* Oss 获取服务端签名
* @return
*/
@RequestMapping("/oss/policy")
public R policy() {
// https://gulimall-hello.oss-cn-beijing.aliyuncs.com/hahaha.jpg 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 = 600;
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(StandardCharsets.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);
}
}
这里 的话,因为服务还没做好,所以的话,后面的保存URL我们是没有做的。
这个咱们是直接使用vue+axios+elementui 来做的。
<template>
<div>
<el-upload
:action="dataObj.host"
: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文件,且不超过10MBdiv>
el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="fileList[0].url" alt="">
el-dialog>
div>
template>
<script>
import {getUUID} from "./uuid";
import axios from "[email protected]@axios";
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) => {
axios
.get('/third-part/oss/policy')
.then(response => {
response = response.data;
_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(function (error) {
reject(false);
})
});
},
handleUploadSuccess(res, file) {
console.log("数据获取为",this.dataObj)
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>
<template>
<div>
<el-upload
:action="dataObj.host"
: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 {getUUID} from "./uuid";
import axios from 'axios'
export default {
name: "multiUpload",
props: {
//图片属性数组
value: Array,
//最大上传图片数量
maxCount: {
type: Number,
default: 10
},
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() {
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) => {
axios
.get('/third-part/oss/policy')
.then(response => {
response = response.data;
_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(function (error) {
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>
之后咱们来测试一下
可以看到正常提交,接下来我们在尝试提交一张艺术照片。
接下来设置一些规则,或者处理一下,就好了。
ok,水完了,是艺术不打码,是科学不放弃。