SpringBoot 3.x集成阿里云OSS:文件上传 断点续传 权限控制

SpringBoot 3.x集成阿里云OSS:文件上传/断点续传/权限控制

  • Spring Boot 3.x 集成阿里云 OSS 终极指南
  • 一、环境准备与依赖配置
    • 1. 添加阿里云 OSS SDK 依赖
    • 2. 配置 OSS 连接参数
  • 二、基础文件上传服务
    • 1. OSS 客户端配置
    • 2. 文件上传服务
  • 三、断点续传高级实现
    • 1. 断点续传服务
    • 2. 断点续传恢复机制
  • 四、精细化权限控制
    • 1. STS 临时凭证服务
    • 2. 前端直传签名服务
  • 五、SDK 坑位指南与最佳实践
    • 1. 常见问题解决方案
    • 2. 性能优化配置
    • 3. 安全最佳实践
  • 六、完整文件管理控制器
  • 七、部署与监控
    • 1. 健康检查端点
    • 2. Prometheus 监控指标
  • 八、总结与最佳实践
    • 1. 架构选择建议
    • 2. 安全防护措施
    • 3. 性能优化方案

Spring Boot 3.x 集成阿里云 OSS 终极指南

一、环境准备与依赖配置

1. 添加阿里云 OSS SDK 依赖

<dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.15.1</version>
</dependency>

2. 配置 OSS 连接参数

# application.yml
aliyun:oss:endpoint: oss-cn-hangzhou.aliyuncs.com # 根据实际区域修改access-key-id: your-access-key-idaccess-key-secret: your-access-key-secretbucket-name: your-bucket-namests:role-arn: acs:ram::1234567890123456:role/oss-sts-role # RAM角色ARNpolicy: | # 权限策略{"Version": "1","Statement": [{"Effect": "Allow","Action": ["oss:GetObject","oss:PutObject"],"Resource": ["acs:oss:*:*:your-bucket-name/*"]}]}

二、基础文件上传服务

1. OSS 客户端配置

@Configuration
public class OssConfig {@Value("${aliyun.oss.endpoint}")private String endpoint;@Value("${aliyun.oss.access-key-id}")private String accessKeyId;@Value("${aliyun.oss.access-key-secret}")private String accessKeySecret;@Beanpublic OSS ossClient() {return new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);}
}

2. 文件上传服务

@Service
@Slf4j
public class OssService {private final OSS ossClient;private final String bucketName;public OssService(OSS ossClient, @Value("${aliyun.oss.bucket-name}") String bucketName) {this.ossClient = ossClient;this.bucketName = bucketName;}/*** 简单文件上传* @param file 上传文件* @param objectKey 对象键(OSS路径)* @return 文件URL*/public String uploadFile(MultipartFile file, String objectKey) throws IOException {try (InputStream inputStream = file.getInputStream()) {ossClient.putObject(bucketName, objectKey, inputStream);return generateUrl(objectKey);} catch (Exception e) {log.error("文件上传失败: {}", objectKey, e);throw new OssException("文件上传失败");}}/*** 生成文件URL(带签名)*/private String generateUrl(String objectKey) {Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000); // 1小时有效return ossClient.generatePresignedUrl(bucketName, objectKey, expiration).toString();}
}

三、断点续传高级实现

1. 断点续传服务

@Service
public class ResumableUploadService {private final OSS ossClient;private final String bucketName;public ResumableUploadService(OSS ossClient, @Value("${aliyun.oss.bucket-name}") String bucketName) {this.ossClient = ossClient;this.bucketName = bucketName;}/*** 断点续传上传* @param file 上传文件* @param objectKey 对象键* @return 上传结果*/public UploadResult resumableUpload(MultipartFile file, String objectKey) {try {// 创建上传请求UploadFileRequest request = new UploadFileRequest(bucketName, objectKey,file.getInputStream(),file.getSize());// 配置上传参数request.setPartSize(5 * 1024 * 1024); // 5MB分片request.setTaskNum(5); // 并发线程数request.setEnableCheckpoint(true); // 开启断点记录// 设置断点文件存储位置String checkpointDir = System.getProperty("java.io.tmpdir") + "/oss-checkpoints";request.setCheckpointFile(checkpointDir + "/" + objectKey + ".ucp");// 执行上传UploadFileResult result = ossClient.uploadFile(request);return new UploadResult(generateUrl(objectKey),result.getMultipartUploadResult().getETag(),result.getMultipartUploadResult().getLocation());} catch (Throwable e) {throw new OssException("断点续传失败", e);}}@Data@AllArgsConstructorpublic static class UploadResult {private String fileUrl;private String eTag;private String location;}
}

2. 断点续传恢复机制

public void resumeUpload(String objectKey) {String checkpointFile = getCheckpointFilePath(objectKey);if (new File(checkpointFile).exists()) {UploadFileRequest request = new UploadFileRequest(bucketName, objectKey);request.setCheckpointFile(checkpointFile);try {ossClient.uploadFile(request);} catch (Throwable e) {throw new OssException("续传失败", e);}} else {throw new OssException("未找到断点记录");}
}

四、精细化权限控制

1. STS 临时凭证服务

@Service
public class StsService {@Value("${aliyun.oss.sts.role-arn}")private String roleArn;@Value("${aliyun.oss.sts.policy}")private String policy;@Value("${aliyun.oss.access-key-id}")private String accessKeyId;@Value("${aliyun.oss.access-key-secret}")private String accessKeySecret;/*** 获取STS临时凭证* @param sessionName 会话名称* @param durationSeconds 有效期(秒)* @return STS凭证*/public StsToken getStsToken(String sessionName, long durationSeconds) {DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);IAcsClient client = new DefaultAcsClient(profile);AssumeRoleRequest request = new AssumeRoleRequest();request.setRoleArn(roleArn);request.setRoleSessionName(sessionName);request.setDurationSeconds(durationSeconds);request.setPolicy(policy);try {AssumeRoleResponse response = client.getAcsResponse(request);AssumeRoleResponse.Credentials credentials = response.getCredentials();return new StsToken(credentials.getAccessKeyId(),credentials.getAccessKeySecret(),credentials.getSecurityToken(),credentials.getExpiration());} catch (ClientException e) {throw new StsException("STS获取失败", e);}}@Data@AllArgsConstructorpublic static class StsToken {private String accessKeyId;private String accessKeySecret;private String securityToken;private String expiration;}
}

2. 前端直传签名服务

@Service
public class OssSignatureService {private final OSS ossClient;private final String bucketName;public OssSignatureService(OSS ossClient, @Value("${aliyun.oss.bucket-name}") String bucketName) {this.ossClient = ossClient;this.bucketName = bucketName;}/*** 生成前端直传签名* @param objectKey 对象键* @param expireSeconds 过期时间(秒)* @return 签名信息*/public SignatureInfo generateSignature(String objectKey, long expireSeconds) {Date expiration = new Date(System.currentTimeMillis() + expireSeconds * 1000);// 创建策略PolicyConditions policy = new PolicyConditions();policy.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 104857600); // 100MB限制policy.addConditionItem(PolicyConditions.COND_KEY, objectKey);// 生成签名String postPolicy = ossClient.generatePostPolicy(expiration, policy);String encodedPolicy = BinaryUtil.toBase64String(postPolicy.getBytes());String signature = ossClient.calculatePostSignature(postPolicy);return new SignatureInfo(ossClient.getEndpoint().toString(),bucketName,objectKey,encodedPolicy,signature,expiration);}@Data@AllArgsConstructorpublic static class SignatureInfo {private String endpoint;private String bucket;private String key;private String policy;private String signature;private Date expiration;}
}

五、SDK 坑位指南与最佳实践

1. 常见问题解决方案

问题类型现象解决方案
连接超时上传大文件时超时增加超时时间:
ossClient.setTimeout(300000)
内存溢出大文件上传时OOM使用文件流代替内存流:
request.setUploadFile(filePath)
分片失败分片上传卡死设置合理的分片大小:
request.setPartSize(5 * 1024 * 1024)
签名失效前端直传签名过期签名有效期至少600秒,建议1200秒
权限不足STS操作失败检查RAM角色权限策略

2. 性能优化配置

@Bean
public OSS ossClient(OssProperties properties) {ClientBuilderConfiguration config = new ClientBuilderConfiguration();// 连接池配置config.setMaxConnections(200); // 最大连接数config.setConnectionTimeout(30 * 1000); // 连接超时30sconfig.setSocketTimeout(120 * 1000); // 读写超时120s// 开启HTTP重试config.setMaxErrorRetry(3); // 开启HTTPSconfig.setProtocol(Protocol.HTTPS);return new OSSClientBuilder().build(properties.getEndpoint(), properties.getAccessKeyId(), properties.getAccessKeySecret(),config);
}

3. 安全最佳实践

/*** 安全文件上传验证*/
public void validateFileUpload(MultipartFile file, String objectKey) {// 1. 文件类型验证String contentType = file.getContentType();if (!Arrays.asList("image/jpeg", "image/png").contains(contentType)) {throw new OssException("不支持的文件类型");}// 2. 文件大小验证if (file.getSize() > 10 * 1024 * 1024) { // 10MB限制throw new OssException("文件大小超过限制");}// 3. 文件名安全过滤if (objectKey.contains("..") || objectKey.contains("/")) {throw new OssException("非法文件名");}// 4. 病毒扫描(集成第三方服务)if (!virusScanService.scan(file)) {throw new OssException("文件安全检测未通过");}
}

六、完整文件管理控制器

@RestController
@RequestMapping("/oss")
@RequiredArgsConstructor
public class OssController {private final OssService ossService;private final ResumableUploadService resumableService;private final StsService stsService;private final OssSignatureService signatureService;/*** 普通文件上传*/@PostMapping("/upload")public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file,@RequestParam("path") String path) throws IOException {String objectKey = "uploads/" + path + "/" + file.getOriginalFilename();String url = ossService.uploadFile(file, objectKey);return ResponseEntity.ok(url);}/*** 断点续传接口*/@PostMapping("/resumable-upload")public ResponseEntity<ResumableUploadService.UploadResult> resumableUpload(@RequestParam("file") MultipartFile file,@RequestParam("path") String path) {String objectKey = "uploads/" + path + "/" + file.getOriginalFilename();return ResponseEntity.ok(resumableService.resumableUpload(file, objectKey));}/*** 获取STS临时凭证*/@GetMapping("/sts-token")public ResponseEntity<StsService.StsToken> getStsToken() {String sessionName = "user-" + SecurityUtils.getCurrentUserId();return ResponseEntity.ok(stsService.getStsToken(sessionName, 3600));}/*** 生成前端直传签名*/@GetMapping("/signature")public ResponseEntity<OssSignatureService.SignatureInfo> getSignature(@RequestParam String fileName) {String objectKey = "uploads/user/" + SecurityUtils.getCurrentUserId() + "/" + fileName;return ResponseEntity.ok(signatureService.generateSignature(objectKey, 1200));}
}

七、部署与监控

1. 健康检查端点

@RestController
@RequestMapping("/actuator")
public class OssHealthController {private final OSS ossClient;private final String bucketName;@GetMapping("/oss-health")public ResponseEntity<String> checkOssHealth() {try {boolean exists = ossClient.doesBucketExist(bucketName);return exists ? ResponseEntity.ok("OSS connection is healthy") :ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body("Bucket not found");} catch (Exception e) {return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body("OSS connection failed: " + e.getMessage());}}
}

2. Prometheus 监控指标

@Bean
public MeterRegistryCustomizer<MeterRegistry> ossMetrics(OSS ossClient) {return registry -> {Gauge.builder("oss.connection.count", ossClient, client -> client.getClientConfiguration().getMaxConnections()).description("OSS connection pool size").register(registry);Counter.builder("oss.upload.count").description("Total OSS upload operations").register(registry);};
}

八、总结与最佳实践

1. 架构选择建议

  • 小文件上传:直接使用简单上传接口
  • 大文件上传:使用断点续传(>10MB)
  • 前端直传:使用STS临时凭证或签名直传
  • 敏感文件:服务端中转上传+病毒扫描

2. 安全防护措施

  1. 权限最小化:STS策略只授予必要权限
  2. 文件类型过滤:限制可上传文件类型
  3. 病毒扫描:集成ClamAV等扫描引擎
  4. 访问日志:开启OSS访问日志审计
  5. WAF防护:配置Web应用防火墙规则

3. 性能优化方案

小文件
大文件
前端直传
客户端
直接上传
分片上传
并发上传
OSS服务端合并
签名/STS
绕过应用服务器

通过本方案,可实现安全高效的OSS文件管理,支持从KB到TB级文件的上传需求,同时满足企业级安全合规要求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/89347.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/89347.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

牛客周赛 Round 100

A小红的双排列没什么好说的 直接 1 1 2 2 3 3 4 4……#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<iostream> #include<bits/stdc.h> #define ll long long using namespace std; int n; int main(){ios::sync_with_stdio(false); …

【Dv3Admin】菜单管理集成阿里巴巴自定义矢量图标库

图标选择是后台管理系统中高频功能。相比用 Element UI、Ant Design 等自带的 icon 集&#xff0c;阿里巴巴 iconfont.cn 支持上传和管理自定义图标&#xff0c;并生成矢量字体&#xff0c;便于统一维护和扩展。 本文目标是支持自定义 iconfont 图标的展示和选择&#xff0c;并…

NO.7数据结构树|线索二叉树|树森林二叉树转化|树森林遍历|并查集|二叉排序树|平衡二叉树|哈夫曼树|哈夫曼编码

线索二叉树 线索二叉树的基本概念 为了解决无法直接找到该结点在某种遍历序列中的前驱和后继结点的问题&#xff0c; 出现了线索二叉树。 一个二叉树通过如下的方法“穿起来” &#xff1a; 所有原本为空的右(孩子)指针改为指向该节点在某种遍历序列中的后继&#xff0c; 所有原…

R语言基础| 基本图形绘制(条形图、堆积图、分组图、填充条形图、均值条形图)

目录 一、前言 二、条形图 1. 简单的条形图 2.堆积、分组和填充条形图(柱状图) &#xff08;1&#xff09;堆积图&#xff0c;对Improved进行堆积&#xff0c;注意position“stack” &#xff08;2&#xff09;分组图&#xff0c;对Improved进行分组&#xff0c;注意posit…

SegNet:一种用于图像分割的深度卷积编码器解码器架构

教程/讲解视频点击文末名片1、什么是语义分割&#xff0c;什么是FCN 我们提出了一种新颖且实用的深度全卷积神经网络架构&#xff0c;用于语义像素级分割&#xff0c;命名为SegNet。 语义分割是指为图像中的每个像素分配一个类别标签&#xff08;如道路、天空、汽车&#xff09…

PyTorch 数据加载全攻略:从自定义数据集到模型训练

目录 一、为什么需要数据加载器&#xff1f; 二、自定义 Dataset 类 1. 核心方法解析 2. 代码实现 三、快速上手&#xff1a;TensorDataset 1. 代码示例 2. 适用场景 四、DataLoader&#xff1a;批量加载数据的利器 1. 核心参数说明 2. 代码示例 五、实战&#xff1…

Python--plist文件的读取

Python练习&#xff1a;读取Apple Plist文件 Plist文件简介 ​​定义​​&#xff1a;Apple公司创建的基于XML结构的文件格式​​特点​​&#xff1a;采用XML语法组织数据&#xff0c;可存储键值对、数组等结构化信息文件扩展名​​&#xff1a;.plist应用场景: ​​iOS系统:​…

JAVA几个注解记录

在Java中&#xff0c;Data、AllArgsConstructor和NoArgsConstructor是Lombok库提供的注解&#xff0c;用于自动生成Java类中的样板代码&#xff08;如getter、setter、构造函数等&#xff09;&#xff0c;从而减少冗余代码&#xff0c;提高开发效率。以下是它们的详细功能和使用…

js对象简介、内置对象

对象、内置对象 jarringslee 对象 对象&#xff08;object&#xff09;是js的一种引用数据类型&#xff0c;是一种无序的数据集合“ul”&#xff08;类比于数组&#xff0c;有序的数据集合“ol”&#xff09;。 基本上等于结构体。 对象的声明 //基本方法 let 对象名 {声…

【工程篇】07:如何打包conda环境并拷贝到另一台服务器上

这是一份以名为 qwen2.5-vl 的 Conda 环境为例的详细操作手册&#xff0c;指导您如何将其打包并迁移至另一台服务器。操作手册&#xff1a;迁移 Conda 环境 qwen2.5-vl 至新服务器 本文档将提供两种有效的方法来迁移您的 qwen2.5-vl 环境。请根据您的具体需求和服务器条件选择最…

rustdesk远控电脑替代todesk,平替向日葵等软件

rustdesk网页端远控电脑docker run --restart always \ --privileged \ -p 9000:9000 \ -p 21114:21114 \ -p 21115:21115 \ -p 21116:21116 \ -p 21116:21116/udp \ -p 21117:21117 \ -p 21118:21118 \ -p 21119:21119 \ -e KEYj8muHpzr2HK00zm9D94b1UFkaJ1bEiWsyA1qxb1nOA \ …

板凳-------Mysql cookbook学习 (十二--------1)

第9章 存储例程&#xff0c;触发器和计划事件 326 9.0 概述 326 9.1 创建复合语句对象 329 mysql> -- 恢复默认分隔符 mysql> DELIMITER ; mysql>mysql> DROP FUNCTION IF EXISTS avg_mail_size; Query OK, 0 rows affected (0.02 sec)mysql> DELIMITER $$ mysq…

密码学系列文(3)--分组密码

一、分组密码概述分组密码是许多系统安全的一个重要组成部分&#xff0c;可用于构造&#xff1a;拟随机数生成器流密码消息认证码(MAC)和杂凑函数消息认证技术、数据完整性机构、实体认证协议以及单钥数字签字体制的核心组成部分应用中对于分组密码的要求&#xff1a;安全性运行…

WCDB soci 查询语句

测试代码 #pragma once #include <string> #include <vector>// Assume OperationLog is a struct representing a row in the table struct OperationLog {int id;std::string op_type;std::string op_subtype;std::string details;std::string timestamp; };clas…

lesson16:Python函数的认识

目录 一、为什么需要函数&#xff1f; 1. 拒绝重复造轮子 2. 让代码像句子一样可读 3. 隔离变化&#xff0c;降低维护成本 二、函数的定义&#xff1a;编写高质量函数的5个要素 基本语法框架 1. 函数命名的黄金法则&#xff08;PEP8规范&#xff09; 2. 不可或缺的文档…

通过轮询方式使用LoRa DTU有什么缺点?

在物联网系统中&#xff0c;DTU&#xff08;Data Transfer Unit&#xff09;通常用于通过485或M-Bus等接口抄读子设备的数据&#xff0c;并将这些数据传输到平台侧。然而&#xff0c;如果DTU采用轮询方式与平台通信&#xff0c;会带来一系列问题&#xff0c;尤其是在功耗和系统…

Syntax Error: Error: PostCSS received undefined instead of CSS string

报错&#xff1a;Syntax Error: Error: PostCSS received undefined instead of CSS string npm rebuild node-sass报错&#xff1a;npm i canvas 报错 canvas2.11.2 run install node-pre-gyp install --fallback-to-build --update-binary npm install canvas --canvas_binar…

人工智能之数学基础:概率论和数理统计在机器学习的地位

概率和统计的概念概率统计是各类学科中唯一一门专门研究随机现象的规律性的学科&#xff0c;随机现象的广泛性决定了这一学科的重要性。概率论是数学的分支&#xff0c;它研究的是如何定量描述随机现象及其规律。我们之前经常在天气软件上看到&#xff1a;“今天下雨的概率是95…

第十四章 Stream API

JAVA语言引入了一个流式Stream API,这个API对集合数据进行操作&#xff0c;类似于使用SQL执行的数据库查询&#xff0c;同样可以使用Stream API并行执行操作。Stream和Collection的区别Collection:静态的内存数据结构&#xff0c;强调的是数据。Stream API:和集合相关的计算操作…

Oracle数据库各版本间的技术迭代详解

今天我想和大家聊聊一个我们可能每天都在用&#xff0c;但未必真正了解的技术——Oracle数据库的版本。如果你是企业的IT工程师&#xff0c;可能经历过“升级数据库”的头疼&#xff1b;如果你是业务负责人&#xff0c;可能疑惑过“为什么一定要换新版本”&#xff1b;甚至如果…