> 技术文档 > Java 对接阿里云 OSS 私有读写(开箱即用)_oss私有读写怎么访问

Java 对接阿里云 OSS 私有读写(开箱即用)_oss私有读写怎么访问

在现代应用开发中,对象存储服务(OSS)是处理图片、视频、文档等非结构化数据的常用方式。阿里云 OSS(Object Storage Service) 提供了高可靠、高可用的对象存储服务,适用于各种企业级应用场景。

本文将详细介绍如何使用 Java SDK 对接阿里云 OSS,并实现私有读写的操作。内容包括:

  • 阿里云 OSS 简介
  • 为什么选择私有 Bucket?
  • ACL 权限详解:Public Read/Write vs Private
  • 准备工作:开通服务与获取密钥
  • Maven 依赖配置
  • Java 实现上传、下载、删除文件
  • 使用签名 URL 实现私有访问(重点讲解原理和优势)
  • 安全建议与最佳实践

🔍 一、阿里云 OSS 简介

阿里云 OSS 是一个海量、安全、低成本、高可靠的云端对象存储服务。支持任意形式的文件上传和下载,广泛用于网站托管、图片资源管理、日志存储、大数据分析等地方。

默认情况下,OSS 的 Bucket 权限为私有(Private),即只有授权用户可以访问。本文重点介绍如何在 Java 应用中对接私有 Bucket,实现文件的安全上传与受控访问。


🧩 二、为何要选择“私有读写”?

1. 什么是 ACL?

ACL(Access Control List)是阿里云 OSS 中用于控制 Bucket 和 Object 访问权限的一种机制。常见的类型包括:

类型 描述 private 默认值,仅 Bucket 拥有者可读写 public-read 所有人可读,Bucket 拥有者可写 public-read-write 所有人可读写(非常不推荐)

2. 为什么要使用 private

  • 安全性高:只有通过授权(如 AccessKey 或签名 URL)才能访问。
  • ❗️避免误公开:防止敏感文件被外网直接访问。
  • ✅ 控制精细:适合需要身份验证或临时授权访问的场景。
  • ⚠️ 不适合开放资源:如果你希望某些文件对外完全公开,请单独设置签名链接或 CDN 加速。

3. 签名 URL 的好处是什么?

由于私有 Bucket 不允许外部直接访问,我们需要一种机制让特定用户在限定时间内访问文件。这时就用到了 签名 URL(Presigned URL)

特点如下:
特性 描述 时效性 可设置过期时间,比如 5 分钟后失效 权限控制 只能执行指定的操作(GET / PUT) 无需认证 用户不需要拥有阿里云账号即可访问 灵活分享 可用于前端预览、邮件附件下载等场景

📌 总结:签名 URL 是实现私有访问的关键手段,既能保证安全性,又能满足临时访问需求。


⚙️ 三、准备工作

1. 开通阿里云 OSS 服务

前往 阿里云控制台 注册账号并开通 OSS 服务。

2. 创建 Bucket

进入 OSS 控制台 → 新建 Bucket

  • 设置区域(Region)
  • 设置 Bucket 名称
  • 访问权限选择 私有(Private)

3. 获取 AccessKey

进入 RAM 控制台 → 用户管理 → 创建子用户

  • 勾选“编程访问”
  • 授权 AliyunOSSFullAccess 权限(或根据需求自定义权限)
  • 保存 AccessKey ID 和 Secret

4. 设置oss存储桶

Java 对接阿里云 OSS 私有读写(开箱即用)_oss私有读写怎么访问
Java 对接阿里云 OSS 私有读写(开箱即用)_oss私有读写怎么访问
设置了为私有读写权限后 按照下面代码就可以直接调用成功了

✅ 安全提示:不要将 AccessKey 暴露在前端或公共仓库中,建议使用环境变量或配置中心管理。


📦 四、Maven 依赖配置

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

💻 五、Java 实现基本操作

1. 创建AliyunOSSService 服务类 通过sdk上传图片和加密图片

import com.aliyun.oss.*;import com.aliyun.oss.common.auth.CredentialsProvider;import com.aliyun.oss.common.auth.CredentialsProviderFactory;import com.aliyun.oss.common.comm.SignVersion;import com.aliyun.oss.model.ObjectMetadata;import com.aliyun.oss.model.PutObjectRequest;import org.springframework.stereotype.Service;import org.springframework.web.multipart.MultipartFile;import java.io.InputStream;import java.net.MalformedURLException;import java.net.URL;import java.net.URLEncoder;import java.nio.charset.StandardCharsets;import java.util.Arrays;import java.util.Date;import java.util.List;import java.util.UUID;/** * @Author: suhai * @Date: 2025/6/5 * @Description: 使用阿里云 OSS SDK V4.x 的上传服务 私有读写上传文件图片 */@Servicepublic class AliyunOSSService { // todo 后续换成 config配置文件获取 private String endpoint = \"https://oss-cn-chengdu.aliyuncs.com\"; private String bucketName = \"menstrual-back\";//桶名 private String region = \"cn-chengdu\"; // private String maxFileSize = \"10MB\"; String accessKeyId = \"\"; String accessKeySecret = \"\"; /** * 上传文件到OSS * @param file 文件 * @param folder 上传到的文件夹 * @return 文件访问URL */ public String uploadFile(MultipartFile file, String folder) { validateFile(file); ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration(); clientBuilderConfiguration.setSignatureVersion(SignVersion.V4); CredentialsProvider credentialsProvider = CredentialsProviderFactory.newDefaultCredentialProvider(accessKeyId,accessKeySecret); OSS ossClient = OSSClientBuilder.create() .endpoint(endpoint) .credentialsProvider(credentialsProvider) .clientConfiguration(clientBuilderConfiguration) .region(region) .build(); try { String originalFilename = file.getOriginalFilename(); String fileExtension = originalFilename.substring(originalFilename.lastIndexOf(\".\")); String safeFileName = UUID.randomUUID() + fileExtension; // URL 编码,防止非法字符 String encodedFileName = URLEncoder.encode(safeFileName, StandardCharsets.UTF_8.toString()).replace(\"+\", \"%20\"); String filePath = folder + \"/\" + encodedFileName; InputStream inputStream = file.getInputStream(); PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, filePath, inputStream); ObjectMetadata metadata = new ObjectMetadata(); metadata.setHeader(\"Content-Type\", file.getContentType()); putObjectRequest.setMetadata(metadata); ossClient.putObject(putObjectRequest); return generateFileUrl(filePath); } catch (OSSException oe) { throw new RuntimeException(\"OSS错误 - 上传失败: \" + oe.getErrorMessage(), oe); } catch (Exception e) { throw new RuntimeException(\"上传失败: \" + e.getMessage(), e); } finally { if (ossClient != null) { ossClient.shutdown(); } } } /** * 验证文件是否符合要求 */ private void validateFile(MultipartFile file) { long maxSize = parseSize(maxFileSize); if (file.getSize() > maxSize) { throw new RuntimeException(\"文件大小不能超过 \" + maxFileSize); } String originalFilename = file.getOriginalFilename(); if (originalFilename == null || !originalFilename.contains(\".\")) { throw new RuntimeException(\"无效的文件名\"); } String fileExtension = originalFilename.substring(originalFilename.lastIndexOf(\".\") + 1).toLowerCase(); List<String> allowedExtensions = Arrays.asList(\"jpg,jpeg,png,gif,pdf,doc,docx,xls,xlsx\".split(\",\")); if (!allowedExtensions.contains(fileExtension)) { throw new RuntimeException(\"不支持的文件类型,仅支持: \" + String.join(\",\", allowedExtensions)); } } /** * 生成文件访问URL */ private String generateFileUrl(String filePath) { return endpoint.replace(\"https://\", \"https://\" + bucketName + \".\") + \"/\" + filePath; } /** * 将字符串格式的文件大小转换为字节数 */ private long parseSize(String size) { size = size.toUpperCase(); if (size.endsWith(\"KB\")) { return Long.parseLong(size.substring(0, size.length() - 2)) * 1024; } else if (size.endsWith(\"MB\")) { return Long.parseLong(size.substring(0, size.length() - 2)) * 1024 * 1024; } else if (size.endsWith(\"GB\")) { return Long.parseLong(size.substring(0, size.length() - 2)) * 1024 * 1024 * 1024; } else { return Long.parseLong(size); } } //私有读加签名 public String getPrivateReadSignatureUrl(String filePath) throws MalformedURLException { URL urlbase = new URL(filePath); String objetName = urlbase.getPath(); if (objetName.startsWith(\"/\")) { objetName = objetName.substring(1); } CredentialsProvider credentialsProvider = CredentialsProviderFactory.newDefaultCredentialProvider(accessKeyId,accessKeySecret); ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration(); clientBuilderConfiguration.setSignatureVersion(SignVersion.V4); OSS ossClient = OSSClientBuilder.create() .endpoint(endpoint) .credentialsProvider(credentialsProvider) .clientConfiguration(clientBuilderConfiguration) .region(region) .build(); try { Date expiration = new Date(new Date().getTime() + 3600 * 1000L); // 生成以GET方法访问的预签名URL。本示例没有额外请求头,其他人可以直接通过浏览器访问相关内容。 URL url = ossClient.generatePresignedUrl(bucketName, objetName, expiration); return url.toString(); } catch (OSSException oe) { throw new RuntimeException(\"OSS错误 - 上传失败: \" + oe.getErrorMessage(), oe); } catch (ClientException ce) { throw new RuntimeException(\"客户端错误 - 上传失败: \" + ce.getErrorMessage(), ce); } finally { ossClient.shutdown(); } }}

2. 编写controller \"temp\"是需要上传的目录路径 因为是私有读 在上传图片返回的路径后需要给前端签名后的地址 所以还需要调用签名

/** * 文件请求处理 * * @author suhai */@RestControllerpublic class OssFileUploadController{ @Autowired private AliyunOSSService aliyunOSSService; /** * 文件图片上传请求 私有写 */ @CrossOrigin @PostMapping(\"/uploadOSS\") public AjaxResult uploadFileOSS(MultipartFile file) { try { String url = aliyunOSSService.uploadFile(file,\"temp\"); String privateReadSignatureUrl = aliyunOSSService.getPrivateReadSignatureUrl(url); Map<String,Object> map = new HashMap<>(); map.put(\"url\",privateReadSignatureUrl); map.put(\"name\",file.getOriginalFilename()); map.put(\"size\",file.getSize()); return AjaxResult.success(map); } catch (Exception e) { return AjaxResult.error(e.getMessage()); } }}

3. 创建OssUrlCleanerUtil图片处理工具类 保存数据库是不需要签名后的url的 所以在新增跟修改的时候需要去签 查询的时候又需要加签,因为多个图片我是以逗号隔开的所以处理了这种情况

/** * @Author: suhai * @Date: 2025/6/5 * @Description: 对oss进行加签 和 去签 的工具类 */@Componentpublic class OssUrlCleanerUtil { @Autowired private AliyunOSSService aliyunOSSService; /** * 清除URL中的签名参数,只保留基础URL和文件路径。 * @param urlsStr 带有签名参数的完整URL。 * @return 清除签名参数后的URL。 */ public String cleanUrlsToString(String urlsStr) { if (urlsStr == null || urlsStr.trim().isEmpty()) { return \"\"; } // 判断是否包含逗号,决定是多个还是单个URL if (urlsStr.contains(\",\")) { String[] urls = urlsStr.split(\",\"); StringBuilder sb = new StringBuilder(); for (int i = 0; i < urls.length; i++) { String url = urls[i].trim(); String cleaned = removeQueryString(url); sb.append(cleaned); if (i < urls.length - 1) {  sb.append(\",\"); // 拼接逗号 } } return sb.toString(); } else { return removeQueryString(urlsStr.trim()); } } /** * 辅助方法:清除单个URL中的查询字符串(签名部分) */ private String removeQueryString(String url) { int queryIndex = url.indexOf(\"?\"); if (queryIndex != -1) { return url.substring(0, queryIndex); } return url; } //加签访问 public String getSignatureUrl(String urlOrUrls) { if (urlOrUrls == null || urlOrUrls.trim().isEmpty()) { return \"\"; } // 判断是否为多个URL if (urlOrUrls.contains(\",\")) { String[] urls = urlOrUrls.split(\",\"); StringBuilder signedUrls = new StringBuilder(); for (int i = 0; i < urls.length; i++) { String originalUrl = urls[i].trim(); String signedUrl; try {  signedUrl = aliyunOSSService.getPrivateReadSignatureUrl(originalUrl); } catch (MalformedURLException e) {  throw new RuntimeException(\"生成签名URL失败: \" + originalUrl, e); } if (signedUrl != null && !signedUrl.isEmpty()) {  signedUrls.append(signedUrl);  if (i < urls.length - 1) { signedUrls.append(\",\");  } } } return signedUrls.toString(); } else { // 单个URL处理 try { return aliyunOSSService.getPrivateReadSignatureUrl(urlOrUrls.trim()); } catch (MalformedURLException e) { throw new RuntimeException(\"生成签名URL失败: \" + urlOrUrls, e); } } }}

🛡️ 六、安全建议与最佳实践

项目 建议 AccessKey 使用 RAM 子账号的 AK,避免使用主账号 权限控制 按最小权限原则分配策略 签名URL 设置合理过期时间,避免长期暴露 日志审计 启用 OSS 访问日志和 RAM 操作日志 敏感信息 不要硬编码在代码中,使用配置中心或环境变量

🧩 七、扩展功能建议

  • 文件分片上传(大文件)
  • 图片缩略图处理(ImageStyle)
  • 多线程上传优化
  • 结合 Spring Boot 构建 REST API 接口
  • 上传前校验 MIME 类型、大小限制等

📚 总结

本文介绍了 Java 如何通过官方 SDK 对接阿里云 OSS,实现了私有 Bucket 的文件上传、下载、删除以及签名 URL 的生成。通过合理的权限控制和签名机制,我们可以在保证安全的前提下灵活地进行文件操作。

选择 private 权限是为了保护数据安全,而签名 URL 则是实现可控访问的重要工具。两者结合,既能保障数据隐私,又能满足业务需求。


📌 源码地址:[GitHub 示例工程(请自行补充)]

📘 参考文档

  • 阿里云 OSS 官方文档
  • OSS Java SDK GitHub

💬 如果你有任何问题或想了解更深入的内容,欢迎留言交流!