本文目录导读:

我来为您提供一个完整的Spring Boot整合阿里云OSS的案例。
添加依赖
<!-- pom.xml -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.15.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
配置文件
application.yml
server:
port: 8080
spring:
servlet:
multipart:
max-file-size: 50MB
max-request-size: 50MB
# 阿里云OSS配置
aliyun:
oss:
endpoint: oss-cn-hangzhou.aliyuncs.com
access-key-id: your-access-key-id
access-key-secret: your-access-key-secret
bucket-name: your-bucket-name
url-prefix: https://your-bucket-name.oss-cn-hangzhou.aliyuncs.com/
# 可选配置
max-file-size: 10485760 # 10MB
application-dev.yml(可选)
aliyun:
oss:
endpoint: oss-cn-hangzhou.aliyuncs.com
access-key-id: your-dev-access-key
access-key-secret: your-dev-access-secret
bucket-name: dev-bucket
url-prefix: https://dev-bucket.oss-cn-hangzhou.aliyuncs.com/
OSS配置类
package com.example.oss.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 = "aliyun.oss")
public class OssConfig {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
private String urlPrefix;
private Long maxFileSize;
@Bean
public OSS ossClient() {
return new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
}
}
OSS工具类
package com.example.oss.util;
import com.aliyun.oss.OSS;
import com.aliyun.oss.model.*;
import com.example.oss.config.OssConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.PostConstruct;
import java.io.*;
import java.net.URL;
import java.util.Date;
import java.util.UUID;
@Slf4j
@Component
@RequiredArgsConstructor
public class OssUtil {
private final OSS ossClient;
private final OssConfig ossConfig;
/**
* 上传文件到OSS
*/
public String upload(MultipartFile file) {
// 校验文件大小
if (file.getSize() > ossConfig.getMaxFileSize()) {
throw new RuntimeException("文件大小超出限制");
}
// 生成文件路径
String originalFilename = file.getOriginalFilename();
String filePath = generateFilePath(originalFilename);
try {
// 创建上传请求
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(file.getSize());
metadata.setContentType(file.getContentType());
// 上传文件
ossClient.putObject(ossConfig.getBucketName(), filePath, file.getInputStream(), metadata);
// 返回文件访问URL
return ossConfig.getUrlPrefix() + filePath;
} catch (IOException e) {
log.error("文件上传失败", e);
throw new RuntimeException("文件上传失败: " + e.getMessage());
}
}
/**
* 上传文件到指定目录
*/
public String upload(MultipartFile file, String directory) {
String originalFilename = file.getOriginalFilename();
String filePath = buildFilePath(directory, originalFilename);
try {
ossClient.putObject(ossConfig.getBucketName(), filePath, file.getInputStream());
return ossConfig.getUrlPrefix() + filePath;
} catch (IOException e) {
log.error("文件上传失败", e);
throw new RuntimeException("文件上传失败: " + e.getMessage());
}
}
/**
* 字节数组上传
*/
public String upload(byte[] data, String fileName) {
String filePath = generateFilePath(fileName);
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(data)) {
ossClient.putObject(ossConfig.getBucketName(), filePath, inputStream);
return ossConfig.getUrlPrefix() + filePath;
} catch (IOException e) {
log.error("文件上传失败", e);
throw new RuntimeException("文件上传失败: " + e.getMessage());
}
}
/**
* 下载文件到本地
*/
public void download(String filePath, String localFilePath) {
try {
ossClient.getObject(
new GetObjectRequest(ossConfig.getBucketName(), filePath),
new File(localFilePath)
);
} catch (Exception e) {
log.error("文件下载失败", e);
throw new RuntimeException("文件下载失败: " + e.getMessage());
}
}
/**
* 下载文件为字节数组
*/
public byte[] download(String filePath) {
try {
OSSObject ossObject = ossClient.getObject(ossConfig.getBucketName(), filePath);
try (InputStream inputStream = ossObject.getObjectContent();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
return outputStream.toByteArray();
}
} catch (IOException e) {
log.error("文件下载失败", e);
throw new RuntimeException("文件下载失败: " + e.getMessage());
}
}
/**
* 删除文件
*/
public void delete(String filePath) {
try {
// 从URL中提取文件路径
String objectPath = filePath;
if (filePath.startsWith(ossConfig.getUrlPrefix())) {
objectPath = filePath.substring(ossConfig.getUrlPrefix().length());
}
ossClient.deleteObject(ossConfig.getBucketName(), objectPath);
log.info("文件删除成功: {}", objectPath);
} catch (Exception e) {
log.error("文件删除失败", e);
throw new RuntimeException("文件删除失败: " + e.getMessage());
}
}
/**
* 批量删除文件
*/
public void deleteBatch(String... filePaths) {
try {
for (String filePath : filePaths) {
delete(filePath);
}
} catch (Exception e) {
log.error("批量删除文件失败", e);
throw new RuntimeException("批量删除文件失败: " + e.getMessage());
}
}
/**
* 生成签名URL(临时访问权限)
*/
public String generatePresignedUrl(String filePath, long expiresInSeconds) {
Date expiration = new Date(System.currentTimeMillis() + expiresInSeconds * 1000);
URL url = ossClient.generatePresignedUrl(ossConfig.getBucketName(), filePath, expiration);
return url.toString();
}
/**
* 判断文件是否存在
*/
public boolean doesObjectExist(String filePath) {
return ossClient.doesObjectExist(ossConfig.getBucketName(), filePath);
}
/**
* 获取文件元数据
*/
public ObjectMetadata getObjectMetadata(String filePath) {
return ossClient.getObjectMetadata(ossConfig.getBucketName(), filePath);
}
/**
* 生成文件路径
*/
private String generateFilePath(String originalFilename) {
// 获取文件扩展名
String fileExtension = "";
if (StringUtils.hasText(originalFilename) && originalFilename.contains(".")) {
fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
}
// 生成新的文件名
String newFileName = UUID.randomUUID().toString().replace("-", "") + fileExtension;
// 按日期分目录存储:yyyy/MM/dd/UUID.ext
String datePath = new java.text.SimpleDateFormat("yyyy/MM/dd").format(new Date());
return datePath + "/" + newFileName;
}
/**
* 构建文件路径
*/
private String buildFilePath(String directory, String filename) {
// 清理目录路径
directory = directory.replaceAll("^/+|/+$", "");
String datePath = new java.text.SimpleDateFormat("yyyy/MM/dd").format(new Date());
// 生成新的文件名
String fileExtension = "";
if (StringUtils.hasText(filename) && filename.contains(".")) {
fileExtension = filename.substring(filename.lastIndexOf("."));
}
String newFileName = UUID.randomUUID().toString().replace("-", "") + fileExtension;
return directory + "/" + datePath + "/" + newFileName;
}
}
Service层
package com.example.oss.service;
import com.example.oss.util.OssUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
@Service
@RequiredArgsConstructor
public class FileService {
private final OssUtil ossUtil;
/**
* 上传文件
*/
public String uploadFile(MultipartFile file) {
validateFile(file);
return ossUtil.upload(file);
}
/**
* 上传到指定目录
*/
public String uploadFileToDirectory(MultipartFile file, String directory) {
validateFile(file);
return ossUtil.upload(file, directory);
}
/**
* 批量上传
*/
public List<String> uploadFiles(List<MultipartFile> files) {
List<String> urls = new ArrayList<>();
for (MultipartFile file : files) {
urls.add(uploadFile(file));
}
return urls;
}
/**
* 删除文件
*/
public void deleteFile(String fileUrl) {
ossUtil.delete(fileUrl);
}
/**
* 下载文件
*/
public byte[] downloadFile(String filePath) {
return ossUtil.download(filePath);
}
/**
* 校验文件
*/
private void validateFile(MultipartFile file) {
if (file == null || file.isEmpty()) {
throw new RuntimeException("文件不能为空");
}
// 校验文件大小
long maxSize = 10 * 1024 * 1024; // 10MB
if (file.getSize() > maxSize) {
throw new RuntimeException("文件大小不能超过10MB");
}
// 校验文件类型
String contentType = file.getContentType();
if (!isAllowedFileType(contentType)) {
throw new RuntimeException("不支持的文件类型");
}
}
/**
* 判断文件类型是否支持
*/
private boolean isAllowedFileType(String contentType) {
// 允许的文件类型
String[] allowedTypes = {
"image/jpeg", // .jpg
"image/png", // .png
"image/gif", // .gif
"application/pdf", // .pdf
"application/msword", // .doc
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", // .docx
"application/vnd.ms-excel", // .xls
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", // .xlsx
"text/plain", // .txt
"video/mp4", // .mp4
"audio/mpeg" // .mp3
};
for (String type : allowedTypes) {
if (type.equals(contentType)) {
return true;
}
}
return false;
}
}
Controller层
package com.example.oss.controller;
import com.example.oss.service.FileService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/file")
@RequiredArgsConstructor
public class FileController {
private final FileService fileService;
/**
* 上传文件
*/
@PostMapping("/upload")
public ResponseEntity<Map<String, String>> upload(@RequestParam("file") MultipartFile file) {
String url = fileService.uploadFile(file);
return ResponseEntity.ok(Map.of("url", url));
}
/**
* 上传到指定目录
*/
@PostMapping("/upload/{directory}")
public ResponseEntity<Map<String, String>> uploadToDirectory(
@RequestParam("file") MultipartFile file,
@PathVariable("directory") String directory) {
String url = fileService.uploadFileToDirectory(file, directory);
return ResponseEntity.ok(Map.of("url", url));
}
/**
* 批量上传
*/
@PostMapping("/upload/batch")
public ResponseEntity<Map<String, Object>> uploadBatch(
@RequestParam("files") List<MultipartFile> files) {
List<String> urls = fileService.uploadFiles(files);
return ResponseEntity.ok(Map.of("urls", urls));
}
/**
* 删除文件
*/
@DeleteMapping("/delete")
public ResponseEntity<Void> delete(@RequestParam("fileUrl") String fileUrl) {
fileService.deleteFile(fileUrl);
return ResponseEntity.noContent().build();
}
/**
* 下载文件
*/
@GetMapping("/download")
public ResponseEntity<byte[]> download(@RequestParam("filePath") String filePath) {
byte[] data = fileService.downloadFile(filePath);
String filename = filePath.substring(filePath.lastIndexOf("/") + 1);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + filename + "\"")
.body(data);
}
/**
* 获取文件访问URL
*/
@GetMapping("/url")
public ResponseEntity<Map<String, String>> getFileUrl(
@RequestParam("filePath") String filePath) {
String url = fileService.getFileUrl(filePath);
return ResponseEntity.ok(Map.of("url", url));
}
}
全局异常处理
package com.example.oss.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import java.util.Map;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<Map<String, String>> handleRuntimeException(RuntimeException e) {
log.error("运行时异常", e);
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(Map.of("message", e.getMessage()));
}
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ResponseEntity<Map<String, String>> handleMaxUploadSizeExceededException(
MaxUploadSizeExceededException e) {
log.error("文件大小超出限制", e);
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(Map.of("message", "文件大小超出限制"));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, String>> handleException(Exception e) {
log.error("系统异常", e);
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of("message", "系统异常,请稍后重试"));
}
}
补充Service方法
// 在FileService中添加这些方法
public String getFileUrl(String filePath) {
return ossUtil.getFileUrl(filePath);
}
在OssUtil中添加方法
/**
* 获取文件公开访问URL
*/
public String getFileUrl(String filePath) {
// 如果文件路径已经是完整URL
if (filePath.startsWith("http://") || filePath.startsWith("https://")) {
return filePath;
}
// 去除路径开头的斜杠
filePath = filePath.replaceAll("^/+", "");
return ossConfig.getUrlPrefix() + filePath;
}
/**
* 生成临时签名URL
*/
public String generatePresignedUrl(String filePath, long expiresInMinutes) {
Date expiration = new Date(System.currentTimeMillis() + expiresInMinutes * 60 * 1000);
URL url = ossClient.generatePresignedUrl(ossConfig.getBucketName(), filePath, expiration);
return url.toString();
}
使用示例
// 测试上传
@RestController
@RequiredArgsConstructor
public class TestController {
private final FileService fileService;
@PostMapping("/test/upload")
public String testUpload(@RequestParam("file") MultipartFile file) {
return fileService.uploadFile(file);
}
@DeleteMapping("/test/delete")
public String testDelete(@RequestParam("fileUrl") String fileUrl) {
fileService.deleteFile(fileUrl);
return "删除成功";
}
}
前端调用示例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">OSS文件上传</title>
</head>
<body>
<form id="uploadForm">
<input type="file" name="file" id="file">
<button type="button" onclick="upload()">上传</button>
</form>
<div id="result" style="margin-top: 20px;"></div>
<script>
function upload() {
const fileInput = document.getElementById('file');
const file = fileInput.files[0];
if (!file) {
alert('请选择文件');
return;
}
const formData = new FormData();
formData.append('file', file);
fetch('/api/file/upload', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
document.getElementById('result').innerHTML =
'上传成功:<a href="' + data.url + '" target="_blank">' + data.url + '</a>';
})
.catch(error => {
console.error('上传失败:', error);
alert('上传失败');
});
}
</script>
</body>
</html>
注意事项
-
Security凭证:不要在生产环境代码中直接写明AccessKey,建议使用环境变量或配置中心
-
文件校验:必须校验文件类型和大小,防止恶意上传
-
错误处理:完善的异常处理机制,包括网络异常、OSS服务异常等
-
日志记录:记录关键操作日志,便于问题排查
-
性能优化:对于大文件,可以考虑使用分片上传
-
权限控制:根据业务需求配置Bucket的访问权限(私有或公共读)
-
成本控制:设置生命周期规则,定期清理无用文件
-
安全配置:启用HTTPS,对敏感数据加密存储
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播