基于AmazonS3协议的OSS通用组件,minio8集成(附仓库)
基于AmazonS3协议的OSS存储通用组件客户端 + Docker + MinIO8
使用docker + minio8完成业务集成
前言
公司内部新系统开发中, 对于文件的设计考虑使用minio完成文件的上传下载,公司内部用性能啥的在docker里起个单机的minio得了, 因为minio8 与 8 之前API及docker 的某些命令不同, 使用时遇到一些坑
优化
最近看了有个“真”大佬的分享, 对于一个公司来说OSS服务可能使用云或者自建, 对于项目来说不断的更换对应服务商或者minio的sdk来说都是十分不合理的, 由于现在大部分服务商以及minio都是基于Amazon的S3协议, 那么可以通过直接使用Amazon的客户端一套sdk操作所有基于该协议的OSS服务, 考虑到公司所有项目完全基于springboot, 则进一步封装优化该OSS需求, 使用starter方式封装, 该文档此前处理使用的minio sdk说明保留
common-oss-spring-boot-starter
主要依赖, 其他使用到springboot基础依赖, lombok等
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.3.7.RELEASE</version><relativePath/></parent><dependency><groupId>com.amazonaws</groupId><artifactId>aws-java-sdk-s3</artifactId><version>1.12.267</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId><optional>true</optional></dependency><!--以下非必需--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.1-jre</version></dependency>
META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.hp.oss.OssAutoConfiguration
OssAutoConfiguration
@Configuration
@EnableConfigurationProperties(OssProperties.class)
public class OssAutoConfiguration {@Bean@ConditionalOnMissingBean(S3OssClient.class)public OssClient ossClient(AmazonS3 amazonS3){return new S3OssClient(amazonS3);}@Bean@ConditionalOnMissingBean(AmazonS3.class)@ConditionalOnProperty(prefix = "oss",name = "enable",havingValue = "true")public AmazonS3 amazonS3(OssProperties ossProperties){final long count = Stream.builder().add(ossProperties.getEndpoint()).add(ossProperties.getAccessSecret()).add(ossProperties.getAccessKey()).build().filter(Objects::isNull).count();if (count > 0) {throw new RuntimeException("OSS配置错误,Endpoint,secret,key配置不能为空,请检查");}final BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(ossProperties.getAccessKey(), ossProperties.getAccessSecret());final AWSStaticCredentialsProvider awsStaticCredentialsProvider = new AWSStaticCredentialsProvider(basicAWSCredentials);return AmazonS3Client.builder().withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(ossProperties.getEndpoint(),ossProperties.getRegion())).withCredentials(awsStaticCredentialsProvider).disableChunkedEncoding().withPathStyleAccessEnabled(ossProperties.isPathStyleAccess()).build();}
}
OssProperties
@ConfigurationProperties(prefix = "oss")
@Setter
@Getter
public class OssProperties {private boolean enable = true;private String accessKey;private String accessSecret;/*** 如果是服务器MinIO等直接使用 [$schema]://[$ip]:[$port]* 外网[$Schema]://[$Bucket].[$Endpoint]/[$Object]** https://help.aliyun.com/document_detail/375241.html**/private String endpoint;/*** refres to com.amazonaws.regions.Regions** https://help.aliyun.com/document_detail/31837.htm?spm=a2c4g.11186623.0.0.695178eb0nD6jp**/private String region;private boolean pathStyleAccess = true;
}
定义提供使用的客户端
OssClient
/*** OSS客户端** @author HP* @date 2022/8/25*/
public interface OssClient {void createBucket(String bucketName);void bucketPolicy(String bucketName,String policy);String getObjectURL(String bucketName,String objectName);S3Object getObject(String bucketName,String objectName);PutObjectResult putObject(String bucketName, String objectName, InputStream inputStream, long size, String contentType) throws IOException;AmazonS3 getS3Client();default PutObjectResult putObject(String bucketName,String objectName,InputStream inputStream) throws IOException{return putObject(bucketName,objectName,inputStream,inputStream.available(),"application/octet-stream");}
}
S3客户端的实现
S3OssClient
/*** 亚马逊s3协议客户端** @author HP* @date 2022/8/25*/
@RequiredArgsConstructor
public class S3OssClient implements OssClient {private final AmazonS3 amazonS3;@Overridepublic void createBucket(String bucketName) {if (amazonS3.doesBucketExistV2(bucketName)) {return;}amazonS3.createBucket(bucketName);}@Overridepublic void bucketPolicy(String bucketName,String policy){amazonS3.setBucketPolicy(bucketName, policy);}@Overridepublic String getObjectURL(String bucketName, String objectName) {final URL url = amazonS3.getUrl(bucketName, objectName);return url.toString();}@Overridepublic S3Object getObject(String bucketName, String objectName) {return amazonS3.getObject(bucketName, objectName);}@Overridepublic PutObjectResult putObject(String bucketName, String objectName, InputStream inputStream, long size, String contentType) throws IOException {ObjectMetadata metaData = new ObjectMetadata();metaData.setContentLength(size);metaData.setContentType(contentType);final PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, inputStream, metaData);putObjectRequest.getRequestClientOptions().setReadLimit((int) (size + 1));return amazonS3.putObject(putObjectRequest);}@Overridepublic AmazonS3 getS3Client() {return amazonS3;}
}
上述接口中提供了原生的AmazonS3客户端, 可以使用这个客户端做接口未定义的方法, 主要是封装了最常用的方法, 测试也可以通过bucketPolicy接口对minio8的bucketpolicy做自定义修改;
配置文件 yml方式
由于示例代码(仓库)中配置类默认是 enable=false 所以直接引入模块在spring管理的bean中直接注入会报错,找不到bean的问题, 可以自行将配置文件中的enable默认改为true或者记得在配置文件中配置为true
# Minio配置
oss:enable: trueendpoint: http://192.168.0.192:9900region:accessKey: 1H7LcuGNS2jIkIemaccessSecret: mLgBgaxuJNbCnYVYdapsOeAZ1g6RhY8KbucketName: jsoup-bucketpathStyleAccess: true # 改字段未具体实现功能
`对于accessKey, accessSecret 在 MiniIO中如下配置, 并非使用后台登录/容器启动时配置的账号密码, 其他云平台基本相同(略)
镜像
docker search minio
#结果 minio/minio
#拉取新版镜像 目前最新的是8版本的 ui啥的比较新
docker pull minio/minio
#拉取旧版镜像 很单一的一个ui控制台的那版
docker pull minio/minio:RELEASE.2021-06-17T00-10-46Z
容器
-d --restart=always --privileged=true看情况加
新版(8x版本)
docker run -p 9900:9000 -p 9990:9090 --name myminio \-e "MINIO_ACCESS_KEY=root" \ #至少3位 等于账号-e "MINIO_SECRET_KEY=12345678" \ #至少8位 等于密码-v /Users/programmer/docker/minIO/data:/data \ #挂载数据目录到宿主机-v /Users/programmer/docker/minIO/config:/etc/minio \ #挂载配置目录到宿主机minio/minio server --console-address ":9990" --config-dir /etc/minio /data
--console-address ":9090", 新版控制台需要指定具体端口,并且要将端口映射出来,否则是随机分配
#执行结果
WARNING: MINIO_ACCESS_KEY and MINIO_SECRET_KEY are deprecated.Please use MINIO_ROOT_USER and MINIO_ROOT_PASSWORD
API: http://172.17.0.2:9000 http://127.0.0.1:9000Console: http://172.17.0.2:9090 http://127.0.0.1:9090Documentation: https://docs.min.io
Finished loading IAM sub-system (took 0.0s of 0.0s to load data).
旧版(7x版本)
docker run -p 9000:9000 --name my-minio \-e "MINIO_ACCESS_KEY=root" \ #至少3位 等于账号-e "MINIO_SECRET_KEY=12345678" \ #至少8位 等于密码-v /Users/programmer/docker/minIO/data:/data \ #挂载数据目录到宿主机-v /Users/programmer/docker/minIO/config:/etc/minio \ #挂载配置目录到宿主机minio/minio:RELEASE.2021-06-17T00-10-46Z server --config-dir /etc/minio /data
--config-dir /etc/minio 指定了新的配置文件目录, 原本是 ~/.monio/config, 如果按本例类似指定了新的配置文件目录, 挂载时请对应新目录
#执行结果
Endpoint: http://172.17.0.2:9000 http://127.0.0.1:9000Browser Access:http://172.17.0.2:9000 http://127.0.0.1:9000
旧版的控制台和api接口使用同一个端口
java
这里如果使用通用包
@RequiredArgsConstructor
@RestController
public class TestOSS {private final OssClient ossClient;public static final String POLICY = "{\n" +//version这里没去管,直接不改" \"Version\": \"2012-10-17\",\n" +" \"Statement\": [\n" +" {\n" +" \"Effect\": \"Allow\",\n" +" \"Principal\": {\n" +" \"AWS\": [\n" +" \"*\"\n" +" ]\n" +" },\n" +//根据具体action类型增加" \"Action\": [\n" +" \"s3:GetBucketLocation\"\n" +" ],\n" +" \"Resource\": [\n" +" \"arn:aws:s3:::test-bucket\"\n" +" ]\n" +" },\n" +" {\n" +" \"Effect\": \"Allow\",\n" +" \"Principal\": {\n" +" \"AWS\": [\n" +" \"*\"\n" +" ]\n" +" },\n" +//根据具体action类型增加" \"Action\": [\n" +//可见action只提供了getObject的权限" \"s3:GetObject\"\n" +" ],\n" +" \"Resource\": [\n" +" \"arn:aws:s3:::test-bucket/*\"\n" +" ]\n" +" }\n" +" ]\n" +"}";@SneakyThrows@PostMapping("/testoss")public R testOss(@RequestParam(value = "file") MultipartFile file) {ossClient.bucketPolicy("test-bucket", POLICY);ossClient.putObject("test-bucket", file.getOriginalFilename(), file.getInputStream());final S3Object object = ossClient.getObject("test-bucket", file.getOriginalFilename());final String objectURL = ossClient.getObjectURL("test-bucket", file.getOriginalFilename());return R.ok(objectURL, object);}
}
使用以上API, 即可完成对主流的几个OSS服务的对接, 不需要针对性的引入各方的sdk
以下使用的是MinIO提供的sdk
config类
对于自定义的配置类,个人还是比较推荐这种实现了InitializingBean的写法, 使用的时候代码看着简洁一些
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioConfig implements InitializingBean {private String endpoint;private String defaultBucketName;private String accessKey;private String secretKey;public static String END_POINT;public static String DEFAULT_BUCKET_NAME;public static String ACCESS_KEY;public static String SECRET_KEY;@Overridepublic void afterPropertiesSet() throws Exception {END_POINT = endpoint;DEFAULT_BUCKET_NAME = defaultBucketName;ACCESS_KEY = accessKey;SECRET_KEY = secretKey;}
}
8之后, API基本上都是使用各种Builder参数, 8之前还是根据需要的参数单独传参.
客户端对象
//新版
minioClient = MinioClient.builder().endpoint(MinioConfig.END_POINT).credentials(MinioConfig.ACCESS_KEY, MinioConfig.SECRET_KEY).build();
//旧版
minioClient = new MinioClient(endpoint, access_key, secret_key);//创建了客户端对象点取api看看也就清楚了
//例如以文件流形式上传文件
public static boolean upload(String bucketName, String fileName, InputStream inputStream) {try {makeBucket(bucketName);minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(inputStream, inputStream.available(), -1).build());return true;} catch (Exception e) {e.printStackTrace();return false;}
}
创建永久的文件url
问题:
临时文件url不赘述,和以前一样; minio8之后不提供getObjectUrl()
的API了, 其实结果也就是 endPoint+/+bucket+/+filename
, 而且getObjectUrl()
也得是bucket
的policy
设置为public
时能访问到文件, 如果单纯将策略设置为public
, bucket
下的文件也可以通过url
遍历出来, 不合适.
解决:
7
的bucket就给了private
和public
两种类型, 8
之后多了一个custom
, 所以正好通过这个custom配置bucket的自定义policy
通过控制台将bucket先设置为public保存,然后再改为custom可以获得public的policy配置, 通过该配置删除部分权限达到目的.
因为bucket跟日期相关, 我选择在每次创建bucket的时候设置此策略为默认策略
代码实现
${bucketName} 需要自行替换为bucket名称
/*** 默认自定义策略,不可以通过url直接查询bucket下所有文件*/private static final String DEFAULT_POLICY = "{\n" +//version这里没去管,直接不改" \"Version\": \"2012-10-17\",\n" +" \"Statement\": [\n" +" {\n" +" \"Effect\": \"Allow\",\n" +" \"Principal\": {\n" +" \"AWS\": [\n" +" \"*\"\n" +" ]\n" +" },\n" +//根据具体action类型增加" \"Action\": [\n" +" \"s3:GetBucketLocation\"\n" +" ],\n" +" \"Resource\": [\n" +" \"arn:aws:s3:::${bucketName}\"\n" +" ]\n" +" },\n" +" {\n" +" \"Effect\": \"Allow\",\n" +" \"Principal\": {\n" +" \"AWS\": [\n" +" \"*\"\n" +" ]\n" +" },\n" +//根据具体action类型增加" \"Action\": [\n" +//可见action只提供了getObject的权限" \"s3:GetObject\"\n" +" ],\n" +" \"Resource\": [\n" +" \"arn:aws:s3:::${bucketName}/*\"\n" +" ]\n" +" }\n" +" ]\n" +"}";/*** 设置bucket策略** @param bucketName bucket名称*/public static void bucketPolicy(String bucketName) {try {minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(processPolicy(bucketName)).build());} catch (Exception e) {e.printStackTrace();}}/*** minio 8.x.x后版本不再提供getObjectUrl方法* 实际返回就是 endpoint + bucket + filename** @param bucketName bucket名称* @param fileName 文件名* @return 获取文件的url contentType = application/octet-stream*/public static String fileUrl(String bucketName, String fileName) {try {return MinioConfig.END_POINT + "/" + bucketName + "/" + fileName;} catch (Exception e) {throw new RuntimeException("获取文件url异常:" + e.getMessage());}}
当访问到目录时提示无权限, 当访问具体文件时可以通过固定的url直接下载文件
curl 192.168.0.192:9100/2022-05/
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>Access Denied.</Message>
<BucketName>2022-05</BucketName>
<Resource>/2022-05/</Resource>
<RequestId>16F1AD3A9B8424EB</RequestId>
<HostId>fa36963f-b9d4-4dfe-86e3-db649276277d</HostId>
</Error>%
仓库
https://github.com/hp-coder/common-starters.git
基于AmazonS3协议的OSS通用组件,minio8集成(附仓库)相关推荐
- (TCP-over-UDP library)基于UDP协议之上实现通用、可靠、高效的TCP协议
随着互联网应用广泛推广,出现了越来越多的网络应用,其中基于p2p思想的各种网络技术的产品也越来越多的出现在我们的视野当中.从最早闻名的Napster到现在的Bittorrent.eMule.skype ...
- 【WSN】基于COMPOW协议下的网络连通率和覆盖率附matlab代码
1 简介 COMPOW (COMMON POWER)协议是一种简单的将功率控制与路由协议相结合的解决方案,其基本思想是:所有的传感器节点使用一致的发射功率,在保证网络连通的前提下将功率最小化.COMP ...
- 全端通用快速开发UI组件库UnifyUi大更新,Unify Ui是基于uni-app的全端(vue/nvue)组件库
本次进行整个框架的ui组件结构,添加详细注释.优化部分组件,新增 暗黑模式 是不是有种剁手的感觉?点击 unifyui 时刻关注动态, unifyui 团队即将公开招募协作者共同完善unifyui! ...
- vue结合饿了么_饿了么基于Vue2.0的通用组件开发之路(分享会记录)
Element:一套通用组件库的开发之路 Element 是由饿了么UED设计.饿了么大前端开发的一套基于 Vue 2.0 的桌面端组件库.今天我们要分享的就是开发 Element 的一些心得. 官网 ...
- 电信运营商基于 MQTT 协议 构建千万级 IoT 设备管理平台
MQTT 是用于物联网的标准消息传递协议.它被设计为一种非常轻量级的发布/订阅消息传送,非常适合以较小的代码占用量和网络带宽连接远程设备.MQTT 协议具有以下特点: 轻巧高效:MQTT 客户端非常小 ...
- iOS开发笔记--基于面向协议MVP模式下的软件设计
传统模式下的开发 MVC MVVM 基于面向协议MVP的介绍 MVP实战开发 说在前面: 相信就算你是个iOS新手也应该听说过MVC的,MVC是构建iOS App的标准模板.随着时间的推移,在iOS ...
- 基于c++实现RTSP/RTMP推流组件PushStream简介
技术在于交流.沟通,转载请注明出处并保持作品的完整性. 原文:https://blog.csdn.net/hiwubihe/article/details/84639975 [本系列相关文章] 基于c ...
- 移动端cube界面设计html,滴滴开源基于 Vue.js 的移动端组件库 cube-ui
原标题:滴滴开源基于 Vue.js 的移动端组件库 cube-ui 开源最前线(ID:OpenSourceTop) 猿妹 整编 综合自:https://didi.github.io/cube-ui/ ...
- django 1.8 官方文档翻译: 3-4-2 基于类的内建通用视图
基于类的内建通用视图 编写Web应用可能是单调的,因为你需要不断的重复某一种模式. Django尝试从model和 template层移除一些单调的情况,但是Web开发者依然会在view(视图)层经历 ...
最新文章
- 年终收藏!一文看尽2020年度最出圈AI论文合集
- php$_GET 变量
- Angular应用一个创建场景的问题分析
- springboot中得注解_Spring以及SpringBoot中的常用的注解小结
- python将数据集分成训练样本和类标签
- java抓取网页数据_实现网络图片爬虫,只需5秒快速把整个网页上的图片全下载打包zip...
- (三.0)通过FPGA实现以太网通信原理及理解
- Qt 调用CUDA静态库和动态库生成与配置
- 几个jquery分发库速度测评
- 用计算机弹刚好一点,《计算机组成原理》作业解答(14级)
- win7从光盘进入修复计算机,怎么用光盘修复win7_win7如何用光盘修复系统
- matlab 音乐 豆腐汤,40岁健康家常菜pdf
- Android Studio系统状态栏,设置setSmallIcon通知图标无效问题及解决方案
- MAC-MAC-MAC-MAC
- IMF: Interactive Multimodal Fusion Model for Link Prediction
- 三星打印机显示无法连接服务器,三星打印机不能打印,提示“无法识别的USB设备”解决办法...
- Python学习,元类type 反射 函数与方法 双下方法
- Ligntning接口破解,小伙伴们再也不用担心ios内核调试了
- kerberos 巨坑
- 太平歌词 - 《白蛇传》
热门文章
- DJI SDK之导入篇--将SDK配置到自己的应用程序中
- X210开发板(S5PV210芯片)uboot移植DM9000驱动移植
- armv8 boot 分析
- 2012“粤嵌杯”芯片应用电子设计比赛成功举办
- 云原生爱好者周刊:买个蓝牙打印机实时打印新提交的 PR 吧 | 2022-10-24
- ATA 条件转移cmp jxx
- It doesn‘t look good for a date“: Transforming Critiques into Preferences for CRS
- Sigmoid非线性激活函数,FM调频,胆机,HDR的意义
- 基于自组织背景减除的运动目标检测算法
- 《西河大鼓——杨家将(灵堂)》(唱词文本)