阿里云oss使用

admin 2026-02-25 17:37:07 2022世界杯直播

阿里云oss使用

阿里云Oss

一、阿里云 OSS 是什么?

阿里云对象存储服务(Object Storage Service,简称 OSS) 是阿里云提供的海量、安全、低成本、高可靠的云存储服务。

核心概念:

存储空间(Bucket):存储对象的容器,每个对象都必须包含在某个存储空间中

对象(Object):OSS 存储的基本单元,可以是任意类型的文件

地域(Region):OSS 数据中心的物理位置

访问域名(Endpoint):OSS 对外服务的访问地址

AccessKey:访问 OSS 的密钥,包括 AccessKeyId 和 AccessKeySecret

OSS 的主要优势:

高可靠性:数据持久性不低于 99.9999999999%

高可用性:服务可用性不低于 99.995%

安全性强:支持多种加密和权限控制方式

成本低廉:按实际使用量付费,无最低消费

易于扩展:存储容量无限扩展

二、准备工作

1. 开通 OSS 服务

登录阿里云控制台

搜索并开通 OSS 服务

创建 Bucket(存储空间)

2. 获取 AccessKey

进入阿里云控制台 → 访问控制 RAM

创建用户并授予 OSS 相关权限

获取 AccessKeyId 和 AccessKeySecret

三、Spring Boot 集成 OSS

1. 添加依赖

Maven:

xml

com.aliyun.oss

aliyun-sdk-oss

3.15.1

或者使用阿里云官方 Starter(推荐):

xml

com.alibaba.cloud

spring-cloud-starter-alicloud-oss

2.2.0.RELEASE

2. 配置文件(application.yml)

yaml

# 阿里云 OSS 配置

alibaba:

cloud:

access-key: your-access-key-id # 替换为你的 AccessKeyId

secret-key: your-access-key-secret # 替换为你的 AccessKeySecret

oss:

endpoint: oss-cn-hangzhou.aliyuncs.com # 替换为你的 Bucket 所在区域的 Endpoint

bucket: your-bucket-name # 替换为你的 Bucket 名称

cname: false # 是否使用自定义域名

path-style-access: false # 是否使用路径风格访问

# 自定义配置(可选)

oss:

max-size: 10485760 # 文件大小限制:10MB

allowed-extensions: .jpg,.png,.gif,.pdf,.doc,.docx # 允许的文件扩展名

callback-url: http://your-domain.com/callback # 回调地址(如果需要)

dir-prefix: images/ # 文件存储目录前缀

四、配置类详解

1. 基础配置类

java

package com.example.config;

import com.aliyun.oss.OSS;

import com.aliyun.oss.OSSClientBuilder;

import lombok.Data;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Data

@Configuration

@ConfigurationProperties(prefix = "alibaba.cloud.oss")

public class AliyunOSSConfig {

private String endpoint;

private String accessKey;

private String secretKey;

private String bucket;

/**

* 创建 OSS 客户端

*/

@Bean

public OSS ossClient() {

return new OSSClientBuilder().build(endpoint, accessKey, secretKey);

}

}

2. 完整配置类(推荐)

java

package com.example.config;

import com.aliyun.oss.OSS;

import com.aliyun.oss.OSSClientBuilder;

import com.aliyun.oss.common.comm.Protocol;

import lombok.Data;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import java.util.concurrent.LinkedBlockingQueue;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

@Data

@Configuration

@ConfigurationProperties(prefix = "oss")

public class OSSConfig {

// OSS 基本配置

private String endpoint;

private String accessKey;

private String secretKey;

private String bucket;

// 业务配置

private Long maxSize; // 文件大小限制

private String allowedExtensions; // 允许的文件类型

private String callbackUrl; // 回调地址

private String dirPrefix; // 目录前缀

// 线程池配置(用于异步上传)

private int corePoolSize = 5;

private int maxPoolSize = 20;

private int queueCapacity = 100;

/**

* 创建 OSS 客户端

*/

@Bean

public OSS ossClient() {

// 创建客户端配置

com.aliyun.oss.ClientBuilderConfiguration config =

new com.aliyun.oss.ClientBuilderConfiguration();

// 设置协议(HTTP 或 HTTPS)

config.setProtocol(Protocol.HTTPS);

// 设置连接超时时间(默认50秒)

config.setConnectionTimeout(50000);

// 设置Socket超时时间(默认50秒)

config.setSocketTimeout(50000);

// 设置最大连接数(默认1024)

config.setMaxConnections(1024);

return new OSSClientBuilder().build(endpoint, accessKey, secretKey, config);

}

/**

* 创建文件上传线程池

*/

@Bean("ossThreadPool")

public ThreadPoolExecutor ossThreadPool() {

return new ThreadPoolExecutor(

corePoolSize,

maxPoolSize,

60L,

TimeUnit.SECONDS,

new LinkedBlockingQueue<>(queueCapacity),

new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:由调用线程处理

);

}

/**

* 获取允许的文件扩展名数组

*/

public String[] getAllowedExtensionArray() {

if (allowedExtensions == null || allowedExtensions.trim().isEmpty()) {

return new String[0];

}

return allowedExtensions.split(",");

}

}

五、服务类实现

1. OSS 服务接口

java

package com.example.service;

import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;

import java.util.List;

public interface OSSService {

/**

* 上传文件

*/

String uploadFile(MultipartFile file);

/**

* 上传文件到指定目录

*/

String uploadFile(MultipartFile file, String directory);

/**

* 上传文件(自定义文件名)

*/

String uploadFile(InputStream inputStream, String fileName);

/**

* 批量上传文件

*/

List uploadFiles(List files);

/**

* 下载文件

*/

InputStream downloadFile(String fileName);

/**

* 删除文件

*/

boolean deleteFile(String fileName);

/**

* 批量删除文件

*/

boolean deleteFiles(List fileNames);

/**

* 检查文件是否存在

*/

boolean doesFileExist(String fileName);

/**

* 获取文件访问URL

*/

String getFileUrl(String fileName);

/**

* 获取文件访问URL(带过期时间)

*/

String getFileUrl(String fileName, long expiration);

}

2. OSS 服务实现类

java

package com.example.service.impl;

import com.aliyun.oss.OSS;

import com.aliyun.oss.model.*;

import com.example.config.OSSConfig;

import com.example.service.OSSService;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.scheduling.annotation.Async;

import org.springframework.stereotype.Service;

import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;

import java.io.IOException;

import java.io.InputStream;

import java.net.URL;

import java.util.*;

import java.util.concurrent.CompletableFuture;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.stream.Collectors;

@Slf4j

@Service

public class OSSServiceImpl implements OSSService {

@Autowired

private OSS ossClient;

@Autowired

private OSSConfig ossConfig;

@Autowired

private ThreadPoolExecutor ossThreadPool;

/**

* 上传文件

*/

@Override

public String uploadFile(MultipartFile file) {

return uploadFile(file, ossConfig.getDirPrefix());

}

/**

* 上传文件到指定目录

*/

@Override

public String uploadFile(MultipartFile file, String directory) {

// 参数校验

if (file == null || file.isEmpty()) {

throw new IllegalArgumentException("文件不能为空");

}

// 文件大小校验

if (file.getSize() > ossConfig.getMaxSize()) {

throw new IllegalArgumentException("文件大小不能超过 " + ossConfig.getMaxSize() / 1024 / 1024 + "MB");

}

// 文件类型校验

String originalFilename = file.getOriginalFilename();

String fileExtension = getFileExtension(originalFilename);

if (!isAllowedExtension(fileExtension)) {

throw new IllegalArgumentException("不支持的文件类型: " + fileExtension);

}

try {

// 生成唯一文件名

String fileName = generateFileName(directory, fileExtension);

// 创建上传请求

PutObjectRequest putObjectRequest = new PutObjectRequest(

ossConfig.getBucket(),

fileName,

file.getInputStream()

);

// 设置对象元数据

ObjectMetadata metadata = new ObjectMetadata();

metadata.setContentLength(file.getSize());

metadata.setContentType(getContentType(fileExtension));

putObjectRequest.setMetadata(metadata);

// 上传文件

ossClient.putObject(putObjectRequest);

log.info("文件上传成功: {}", fileName);

return fileName;

} catch (IOException e) {

log.error("文件上传失败: {}", e.getMessage(), e);

throw new RuntimeException("文件上传失败", e);

}

}

/**

* 异步上传文件

*/

@Async

public CompletableFuture uploadFileAsync(MultipartFile file) {

return CompletableFuture.supplyAsync(() -> uploadFile(file), ossThreadPool);

}

/**

* 上传文件(自定义文件名)

*/

@Override

public String uploadFile(InputStream inputStream, String fileName) {

try {

PutObjectRequest putObjectRequest = new PutObjectRequest(

ossConfig.getBucket(),

fileName,

inputStream

);

ossClient.putObject(putObjectRequest);

log.info("文件上传成功: {}", fileName);

return fileName;

} finally {

try {

if (inputStream != null) {

inputStream.close();

}

} catch (IOException e) {

log.error("关闭流失败: {}", e.getMessage());

}

}

}

/**

* 批量上传文件

*/

@Override

public List uploadFiles(List files) {

return files.stream()

.map(this::uploadFile)

.collect(Collectors.toList());

}

/**

* 下载文件

*/

@Override

public InputStream downloadFile(String fileName) {

if (!doesFileExist(fileName)) {

throw new IllegalArgumentException("文件不存在: " + fileName);

}

OSSObject ossObject = ossClient.getObject(ossConfig.getBucket(), fileName);

return ossObject.getObjectContent();

}

/**

* 删除文件

*/

@Override

public boolean deleteFile(String fileName) {

try {

ossClient.deleteObject(ossConfig.getBucket(), fileName);

log.info("文件删除成功: {}", fileName);

return true;

} catch (Exception e) {

log.error("文件删除失败: {}", fileName, e);

return false;

}

}

/**

* 批量删除文件

*/

@Override

public boolean deleteFiles(List fileNames) {

try {

DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(ossConfig.getBucket())

.withKeys(fileNames);

ossClient.deleteObjects(deleteObjectsRequest);

log.info("批量删除文件成功: {}", fileNames);

return true;

} catch (Exception e) {

log.error("批量删除文件失败: {}", fileNames, e);

return false;

}

}

/**

* 检查文件是否存在

*/

@Override

public boolean doesFileExist(String fileName) {

try {

return ossClient.doesObjectExist(ossConfig.getBucket(), fileName);

} catch (Exception e) {

log.error("检查文件是否存在失败: {}", fileName, e);

return false;

}

}

/**

* 获取文件访问URL

*/

@Override

public String getFileUrl(String fileName) {

// 设置URL过期时间为1小时

return getFileUrl(fileName, 3600);

}

/**

* 获取文件访问URL(带过期时间)

*/

@Override

public String getFileUrl(String fileName, long expiration) {

try {

Date expirationDate = new Date(System.currentTimeMillis() + expiration * 1000);

URL url = ossClient.generatePresignedUrl(ossConfig.getBucket(), fileName, expirationDate);

return url.toString();

} catch (Exception e) {

log.error("生成文件URL失败: {}", fileName, e);

return null;

}

}

// ========== 私有方法 ==========

/**

* 生成唯一文件名

*/

private String generateFileName(String directory, String extension) {

String uuid = UUID.randomUUID().toString().replace("-", "");

String timestamp = String.valueOf(System.currentTimeMillis());

if (StringUtils.isNotBlank(directory)) {

if (!directory.endsWith("/")) {

directory += "/";

}

return directory + timestamp + "_" + uuid + extension;

}

return timestamp + "_" + uuid + extension;

}

/**

* 获取文件扩展名

*/

private String getFileExtension(String filename) {

if (StringUtils.isBlank(filename)) {

return "";

}

int lastDotIndex = filename.lastIndexOf(".");

return lastDotIndex >= 0 ? filename.substring(lastDotIndex).toLowerCase() : "";

}

/**

* 检查文件扩展名是否允许

*/

private boolean isAllowedExtension(String extension) {

if (StringUtils.isBlank(extension)) {

return false;

}

String[] allowedExtensions = ossConfig.getAllowedExtensionArray();

if (allowedExtensions.length == 0) {

return true; // 如果没有配置限制,则允许所有类型

}

return Arrays.asList(allowedExtensions).contains(extension.toLowerCase());

}

/**

* 获取Content-Type

*/

private String getContentType(String fileExtension) {

switch (fileExtension.toLowerCase()) {

case ".jpg":

case ".jpeg":

return "image/jpeg";

case ".png":

return "image/png";

case ".gif":

return "image/gif";

case ".pdf":

return "application/pdf";

case ".doc":

return "application/msword";

case ".docx":

return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";

default:

return "application/octet-stream";

}

}

}

六、控制器类

java

package com.example.controller;

import com.example.service.OSSService;

import com.example.utils.Result;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.*;

import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@Slf4j

@RestController

@RequestMapping("/api/oss")

public class OSSController {

@Autowired

private OSSService ossService;

/**

* 单文件上传

*/

@PostMapping("/upload")

public Result uploadFile(@RequestParam("file") MultipartFile file,

@RequestParam(value = "directory", required = false) String directory) {

try {

String fileName;

if (directory != null) {

fileName = ossService.uploadFile(file, directory);

} else {

fileName = ossService.uploadFile(file);

}

String fileUrl = ossService.getFileUrl(fileName);

return Result.success("文件上传成功", fileUrl);

} catch (Exception e) {

log.error("文件上传失败: {}", e.getMessage());

return Result.error(e.getMessage());

}

}

/**

* 多文件上传

*/

@PostMapping("/upload/batch")

public Result> uploadFiles(@RequestParam("files") List files) {

try {

List fileNames = ossService.uploadFiles(files);

List fileUrls = fileNames.stream()

.map(ossService::getFileUrl)

.collect(java.util.stream.Collectors.toList());

return Result.success("文件上传成功", fileUrls);

} catch (Exception e) {

log.error("批量文件上传失败: {}", e.getMessage());

return Result.error(e.getMessage());

}

}

/**

* 删除文件

*/

@DeleteMapping("/delete")

public Result deleteFile(@RequestParam("fileName") String fileName) {

try {

boolean result = ossService.deleteFile(fileName);

return result ?

Result.success("文件删除成功", true) :

Result.error("文件删除失败");

} catch (Exception e) {

log.error("文件删除失败: {}", e.getMessage());

return Result.error(e.getMessage());

}

}

/**

* 获取文件URL

*/

@GetMapping("/url")

public Result getFileUrl(@RequestParam("fileName") String fileName) {

try {

String fileUrl = ossService.getFileUrl(fileName);

return Result.success("获取成功", fileUrl);

} catch (Exception e) {

log.error("获取文件URL失败: {}", e.getMessage());

return Result.error(e.getMessage());

}

}

}

七、工具类和异常处理

1. 统一返回结果

java

package com.example.utils;

import lombok.Data;

import java.io.Serializable;

@Data

public class Result implements Serializable {

private int code;

private String message;

private T data;

private long timestamp;

public Result() {

this.timestamp = System.currentTimeMillis();

}

public static Result success(T data) {

Result result = new Result<>();

result.setCode(200);

result.setMessage("success");

result.setData(data);

return result;

}

public static Result success(String message, T data) {

Result result = new Result<>();

result.setCode(200);

result.setMessage(message);

result.setData(data);

return result;

}

public static Result error(String message) {

Result result = new Result<>();

result.setCode(500);

result.setMessage(message);

return result;

}

public static Result error(int code, String message) {

Result result = new Result<>();

result.setCode(code);

result.setMessage(message);

return result;

}

}

2. 全局异常处理

java

package com.example.handler;

import com.example.utils.Result;

import lombok.extern.slf4j.Slf4j;

import org.springframework.web.bind.annotation.ExceptionHandler;

import org.springframework.web.bind.annotation.RestControllerAdvice;

import org.springframework.web.multipart.MaxUploadSizeExceededException;

@Slf4j

@RestControllerAdvice

public class GlobalExceptionHandler {

/**

* 文件大小超限异常

*/

@ExceptionHandler(MaxUploadSizeExceededException.class)

public Result handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {

log.warn("文件大小超过限制: {}", e.getMessage());

return Result.error(413, "文件大小超过限制");

}

/**

* 非法参数异常

*/

@ExceptionHandler(IllegalArgumentException.class)

public Result handleIllegalArgumentException(IllegalArgumentException e) {

log.warn("参数错误: {}", e.getMessage());

return Result.error(400, e.getMessage());

}

/**

* 其他异常

*/

@ExceptionHandler(Exception.class)

public Result handleException(Exception e) {

log.error("系统异常: {}", e.getMessage(), e);

return Result.error(500, "系统繁忙,请稍后重试");

}

}

八、测试和使用

1. 前端调用示例

html

2. 单元测试

java

package com.example.service;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.mock.web.MockMultipartFile;

import org.springframework.web.multipart.MultipartFile;

import java.nio.charset.StandardCharsets;

@SpringBootTest

class OSSServiceImplTest {

@Autowired

private OSSService ossService;

@Test

void testUploadFile() {

// 创建模拟文件

MultipartFile file = new MockMultipartFile(

"test.txt",

"test.txt",

"text/plain",

"Hello OSS".getBytes(StandardCharsets.UTF_8)

);

try {

String fileName = ossService.uploadFile(file, "test/");

System.out.println("上传成功,文件名: " + fileName);

// 获取文件URL

String fileUrl = ossService.getFileUrl(fileName);

System.out.println("文件URL: " + fileUrl);

// 删除测试文件

ossService.deleteFile(fileName);

} catch (Exception e) {

e.printStackTrace();

}

}

}

九、注意事项

安全性:AccessKey 要妥善保管,不要硬编码在代码中

权限控制:使用 RAM 子账号并授予最小必要权限

网络超时:根据文件大小合理设置超时时间

异常处理:做好网络异常、权限异常等处理

资源释放:及时关闭 InputStream 等资源

费用控制:监控存储量和访问量,避免意外费用

总结

通过以上配置和代码,你可以在 Spring Boot 项目中完整地集成阿里云 OSS 服务,实现文件的上传、下载、删除等功能。这种设计具有良好的扩展性和可维护性,适合在生产环境中使用。

关键点:

使用配置类统一管理 OSS 参数

服务类封装所有 OSS 操作

控制器提供 RESTful API

完善的异常处理和日志记录

支持异步上传提高性能