news 2026/5/25 0:56:02

[SpringBoot 对象存储实战]:预签名 URL 直传 OSS 全流程设计与实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
[SpringBoot 对象存储实战]:预签名 URL 直传 OSS 全流程设计与实现

🔥你好我是fengxin_rou这是我的个人主页fengxin_rou的主页

❄️欢迎查看我的专栏我的专栏

《Java后端学习》、《JAVASE基础》、《JUC并发》、《redis》、《JVM虚拟机》、《MYSQL》、《黑马点评》、《rabbitmq》、《JavaWeb+AI的talis学习系统》、《苍穹外卖》

目录

前言

一、预签名直传:核心原理与优势

1.1 传统上传 vs 预签名直传

1.2 核心优势

1.3 整体流程

二、请求实体设计:标准化上传参数

字段说明

三、OSS 存储服务:核心工具类实现

3.1 头像上传(普通上传示例)

3.2 生成公开访问 URL

3.3 生成预签名 PUT URL(核心)

四、Controller 接口实现:权限校验与业务封装

4.1 权限校验关键点

4.2 返回体结构

五、前端直传执行流程

前端请求示例

结语


前言

在内容社区、自媒体平台等场景中,图片、文件上传是高频需求。 传统后端中转上传会占用大量带宽与内存,高并发下极易成为性能瓶颈。 本文基于 SpringBoot + 阿里云 OSS,实现预签名 URL 客户端直传方案,彻底解放后端服务,兼顾安全、高效与易用。


一、预签名直传:核心原理与优势

预签名 URL是对象存储提供的临时授权机制。 后端用密钥生成带时效、带权限的签名 URL,前端直接用此 URL 上传 / 下载文件,无需透传密钥。

1.1 传统上传 vs 预签名直传

  • 传统上传:客户端→后端→OSS,后端中转流量,压力大、速度慢。
  • 预签名直传:客户端请求签名→后端返回 URL→客户端直传 OSS→通知后端入库。

1.2 核心优势

  1. 服务无压力:后端不处理文件流,仅做授权与记录。
  2. 上传速度快:客户端直连 OSS,带宽不受后端限制。
  3. 安全可控:URL 有时效、有路径、有权限,防止越权与盗传。
  4. 成本更低:减少服务器出口带宽消耗。

1.3 整体流程

  1. 前端请求获取预签名上传 URL。
  2. 后端校验权限、生成路径、返回带签名 URL 与访问 URL。
  3. 前端直接 PUT 上传到 OSS。
  4. 上传完成后通知后端,保存文件 URL 到业务库。

二、请求实体设计:标准化上传参数

统一请求体,明确场景、文件标识、类型,便于权限校验与路径生成。

/** * 预签名直传请求实体 */ public record StoragePresignRequest( @NotBlank String scene, // 上传场景:文章内容/图片 @NotBlank String postId, // 帖子ID(字符串避免精度丢失) @NotBlank String contentType,// 文件类型:image/jpeg、video/mp4 String ext // 文件扩展名 ) {}

字段说明

  • scene:区分业务场景,便于权限控制与目录隔离。
  • postId:关联业务 ID,用于校验归属、防止越权。
  • contentType:必须与前端上传时一致,否则 OSS 验签失败。
  • ext:扩展名,用于生成规范文件路径。

三、OSS 存储服务:核心工具类实现

封装 OSS 基础操作,包括普通上传、生成公开 URL、生成预签名 URL

3.1 头像上传(普通上传示例)

适用于小文件、后端可承接的简单上传场景。

/** * 头像上传到 OSS */ public String uploadAvatar(long userId, MultipartFile file) { // 1.校验 OSS 配置 ensureConfigured(); // 2.提取文件扩展名 String original = file.getOriginalFilename(); String ext = ""; if (original != null && original.contains(".")) { ext = original.substring(original.lastIndexOf(".")); } // 3.生成唯一文件路径 String objectKey = props.getFolder() + "/" + userId + "-" + System.currentTimeMillis() + ext; // 4.创建 OSS 客户端 OSS client = new OSSClientBuilder().build( props.getEndpoint(), props.getAccessKeyId(), props.getAccessKeySecret() ); try { // 5.流式上传 PutObjectRequest request = new PutObjectRequest( props.getBucket(), objectKey, file.getInputStream() ); client.putObject(request); } catch (IOException e) { throw new BusinessException(ErrorCode.BAD_REQUEST, "文件读取失败"); } finally { client.shutdown(); } // 6.返回可访问 URL return publicUrl(objectKey); }

3.2 生成公开访问 URL

拼接 CDN 域名,规范路径,避免双斜杠问题。

/** * 生成公开访问 URL */ private String publicUrl(String objectKey) { if (props.getPublicDomain() != null && !props.getPublicDomain().isBlank()) { return props.getPublicDomain().replaceAll("/$", "") + "/" + objectKey; } return "https://" + props.getBucket() + "." + props.getEndpoint() + "/" + objectKey; }

3.3 生成预签名 PUT URL(核心)

生成前端可直接 PUT 上传的临时 URL,必须指定 ContentType

/** * 生成预签名 PUT 上传 URL */ public String generatePresignedPutUrl( String objectKey, String contentType, int expiresInSeconds ) { ensureConfigured(); OSS client = new OSSClientBuilder().build( props.getEndpoint(), props.getAccessKeyId(), props.getAccessKeySecret() ); try { // 设置过期时间 Date expiration = new Date(System.currentTimeMillis() + expiresInSeconds * 1000L); GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest( props.getBucket(), objectKey, HttpMethod.PUT ); request.setExpiration(expiration); // 必须设置 ContentType,否则前端上传会验签失败 if (contentType != null && !contentType.isBlank()) { request.setContentType(contentType); } URL url = client.generatePresignedUrl(request); return url.toString(); } finally { client.shutdown(); } }

四、Controller 接口实现:权限校验与业务封装

提供对外接口,完成用户认证、权限校验、路径生成、签名返回

/** * 预签名直传接口 */ @PostMapping("/presign") public StoragePresignResponse presign( @Valid @RequestBody StoragePresignRequest request, @AuthenticationPrincipal Jwt jwt ) { // 1.获取当前登录用户ID long userId = jwtService.extractUserId(jwt); // 2.postId 字符串转数字,防止前端精度丢失 long postId; try { postId = Long.parseLong(request.postId()); } catch (NumberFormatException e) { throw new BusinessException(ErrorCode.BAD_REQUEST, "postId 格式非法"); } // 3.校验帖子归属,防止越权上传 KnowPost post = knowPostMapper.findById(postId); if (post == null || !userId.equals(post.getCreatorId())) { throw new BusinessException(ErrorCode.BAD_REQUEST, "无权限操作该草稿"); } // 4.按场景生成文件存储路径 String scene = request.scene(); String ext = normalizeExt(request.ext(), request.contentType(), scene); String objectKey; if ("knowpost_content".equals(scene)) { objectKey = "posts/" + postId + "/content" + ext; } else if ("knowpost_image".equals(scene)) { String date = DateTimeFormatter.ofPattern("yyyyMMdd") .withZone(ZoneId.of("UTC")).format(Instant.now()); String rand = UUID.randomUUID().toString().replace("-", "").substring(0, 8); objectKey = "posts/" + postId + "/images/" + date + "/" + rand + ext; } else { throw new BusinessException(ErrorCode.BAD_REQUEST, "不支持的上传场景"); } // 5.生成10分钟有效期的预签名 URL int expiresIn = 600; String uploadUrl = ossStorageService.generatePresignedPutUrl( objectKey, request.contentType(), expiresIn ); Map<String, String> headers = Map.of("Content-Type", request.contentType()); // 6.返回:上传URL、文件URL、请求头、过期时间 return new StoragePresignResponse(objectKey, uploadUrl, headers, expiresIn); }

4.1 权限校验关键点

  • 必须校验postId 对应用户是否为当前登录用户
  • 无校验会导致越权覆盖、恶意上传、数据污染。
  • 这是系统安全的核心防线

4.2 返回体结构

{ "objectKey": "posts/123/images/20260524/xxxx.jpg", "uploadUrl": "https://xxx.oss-cn-beijing.aliyuncs.com/...?X-Oss-Signature=xxx", "headers": { "Content-Type": "image/jpeg" }, "expireSeconds": 600 }

五、前端直传执行流程

  1. 调用/api/v1/storage/presign获取上传 URL。
  2. 使用PUT方法,携带指定 Content-Type 上传文件。
  3. 上传成功后,将 fileUrl 提交给后端业务接口保存。

前端请求示例

// 获取预签名 const { uploadUrl, headers } = await api.post("/storage/presign", { scene: "knowpost_image", postId: "123", contentType: "image/jpeg", ext: "jpg" }); // 直传 OSS await axios.put(uploadUrl, file, { headers });

结语

本文完整实现 SpringBoot + OSS预签名 URL 直传方案,覆盖请求设计、服务封装、权限校验、接口开放全流程。 该方案大幅降低服务负载,提升上传速度与系统吞吐量,适合文章、图片、视频等大文件上传场景。

实际使用建议:

  1. 预签名有效期控制在5–10 分钟
  2. 按业务场景做目录隔离。
  3. 接入 CDN 加速文件访问。
  4. 增加文件大小、类型、数量限流防护。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/25 0:52:13

2026免费在线去水印保姆级教程!不用下载,3秒去除,一看就会

你是不是也遇到过这种抓狂时刻&#xff1f;在抖音、小红书刷到一个超好看的视频&#xff0c;想保存下来自己收藏或做素材&#xff0c;结果下载下来发现角落顶着个大大的水印&#xff0c;画面瞬间就没了那股质感。更气的是&#xff0c;找了一堆号称“免费去水印”的软件&#xf…

作者头像 李华
网站建设 2026/5/25 0:51:41

权限模型设计完全指南

权限模型设计完全指南 前言 合理的权限模型设计是系统安全的基础&#xff0c;本文介绍常见的权限模型及其实现。 一、权限模型 1.1 RBAC模型 ┌─────────────────────────────────────────────────────┐ │ …

作者头像 李华
网站建设 2026/5/25 0:33:10

Linux chmod、chown、chgrp 权限修改实战(工作必用)

前言权限不会改&#xff0c;服务器寸步难行。本篇讲三个最核心命令&#xff1a;chmod&#xff1a;改 rwx 权限chown&#xff1a;改属主、属组chgrp&#xff1a;只改属组一、chmod 修改权限1. 数字方式&#xff08;最常用&#xff09;bash运行chmod 644 文件 chmod 755 目录 chm…

作者头像 李华
网站建设 2026/5/25 0:32:59

通过curl命令快速测试Taotoken平台的大模型接口连通性

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 通过curl命令快速测试Taotoken平台的大模型接口连通性 在开发或调试过程中&#xff0c;有时我们需要快速验证一个API服务是否可用&…

作者头像 李华
网站建设 2026/5/25 0:32:51

工业AI预测性维护:让设备从“急诊抢救“走向“定期体检“

标签&#xff1a;预测性维护 PHM 故障诊断 RUL预测 工业AI 设备管理一、引言&#xff1a;设备的"算命先生"来了在工厂车间里&#xff0c;设备突然"罢工"的场景每天都在上演。传统维护模式就像急诊抢救——设备坏了才修&#xff0c;生产线被迫停摆&#xff…

作者头像 李华
网站建设 2026/5/25 0:32:09

好用还专业!2026 降AIGC平台测评:最新工具推荐与对比分析

2026年真正好用的AI论文降重与改写工具&#xff0c;核心看降重效果、去AI味、格式保留、学术适配四大指标。综合实测&#xff0c;千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队&#xff0c;覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 …

作者头像 李华