4.6.2 Minio准备工作(非场景启动器第三方集成)
-
引入Minio Maven依赖
在
pom.xml文件增加如下内容:xml<dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.5.7</version> </dependency> -
配置Minio相关参数
在
application.yml中配置Minio的endpoint、accessKey、secretKey、bucketName等参数ymlminio: endpoint: http://<hostname>:<port> access-key: <access-key> secret-key: <secret-key> bucket-name: <bucket-name>注意 :上述
<hostname>、<port>等信息需根据实际情况进行修改。 -
在项目 中创建
com.atguigu.exam.config.properties.MinioProperties,内容如下java@ConfigurationProperties(prefix = "minio") @Data public class MinioProperties { private String endpoint; private String accessKey; private String secretKey; private String bucketName; } -
在common模块 中创建
com.atguigu.exam.config.MinioConfiguration,内容如下java@Configuration @EnableConfigurationProperties(MinioProperties.class) public class MinioConfiguration { @Autowired private MinioProperties properties; @Bean public MinioClient minioClient() { return MinioClient.builder().endpoint(properties.getEndpoint()).credentials(properties.getAccessKey(), properties.getSecretKey()).build(); } }-
@EnableConfigurationProperties(MinioProperties.class)作用 :这个注解用于启用对指定配置属性类(这里是
MinioProperties.class)的支持。如果没有这个注解,即使定义了带有@ConfigurationProperties注解的配置类,Spring 也不会自动将配置文件中的属性绑定到该类的实例上。
-
4.6.3 文件上传服务实现
-
FileUploadService接口
java/** * 文件上传服务 * 支持MinIO和本地文件存储两种方式 */ public interface FileUploadService { /** * 上传文件(自动选择MinIO或本地存储) * @param file 上传的文件 * @param folder 文件夹名称(如:banners, avatars等) * @return 返回图片可访问地址 */ String uploadFile(MultipartFile file,String folder) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException; } -
FileUploadServiceImpl实现类
java@Service @Slf4j public class FileUploadServiceImpl implements FileUploadService { @Autowired private MinioClient minioClient; @Autowired private MinioProperties minioProperties; @Override public String uploadFile(String folder, MultipartFile file) throws Exception { //1. 判断桶是否存在 boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioProperties.getBucketName()).build()); //2. 不存在,创建桶,同时设置访问权限 if (!bucketExists) { //创建桶 minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioProperties.getBucketName()).build()); String config = """ { "Statement" : [ { "Action" : "s3:GetObject", "Effect" : "Allow", "Principal" : "*", "Resource" : "arn:aws:s3:::%s/*" } ], "Version" : "2012-10-17" } """.formatted(minioProperties.getBucketName()); minioClient.setBucketPolicy(SetBucketPolicyArgs.builder() .bucket(minioProperties.getBucketName()) .config(config) .build()); } //3. 处理上传的对象名(影响,minio桶中的文件结构!) //现在: 桶名 / folder / ai.png 缺点: 所有文件都平铺(banner,video)不好区分! 核心缺点,可能覆盖! //小知识点: x/x/x.png -> exam0625 /x/x/ x.png //解决覆盖问题: 确保对象和文件的名字唯一即可!! uuid - - - //1.需要添加文件夹 2.添加uuid确保不重复 String objectName = folder + "/" + new SimpleDateFormat("yyyyMMdd").format(new Date()) + "/" + UUID.randomUUID().toString().replaceAll("-","")+"_"+ file.getOriginalFilename(); log.debug("文件上传核心业务方法,处理后的文件对象名:{}",objectName); //4. 上传文件 putObject方法 //putObject . 上传文件数据 .steam(文件输入流) //uploadObject .上传文件数据 .filename(文件的磁盘地址 c:\\) minioClient.putObject(PutObjectArgs.builder() .bucket(minioProperties.getBucketName()) .contentType(file.getContentType()) .object(objectName) //对象 .stream(file.getInputStream(),file.getSize(),-1) //-1 我们不指定文件切割大小!让minio自动处理! .build()); //5. 拼接回显地址 【端点 + 桶 + 对象名】 String url = String.join("/", minioProperties.getEndpoint(), minioProperties.getBucketName(), objectName); log.info("文件上传核心业务,完成{}文件上传,返回地址为:{}",objectName,url); return url; } }
4.6.4 全局异常处理
在使用上述配置实现功能时,若所有 Controller 层方法都直接写 try - catch 处理异常,会让代码冗余又难维护。而 Spring MVC 全局异常处理 能把所有异常逻辑集中,统一处理各类异常 。这样无需每个 Controller 重复写 try - catch,代码更简洁,后续改异常处理逻辑也只需改一处,维护轻松,让代码结构更清晰合理 。
具体用法如下,详细信息可参考官方文档:
在common模块 中创建com.atguigu.exam.common.GlobalExceptionHandler类,内容如下
java
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public Result exception(Exception e) {
e.printStackTrace();
//记录异常日志
log.error("服务器发生运行时异常!异常信息为:{}",e.getMessage());
//返回对应的提示
return Result.error(e.getMessage());
}
}
上述代码中的关键注解的作用如下
@ControllerAdvice用于声明处理全局Controller方法异常的类
@ExceptionHandler用于声明处理异常的方法,value属性用于声明该方法处理的异常类型
@ResponseBody表示将方法的返回值作为HTTP的响应体
4.7 上传轮播图图接口( post /api/banners/upload-image)

4.7.1 接口分析
接口地址 :/api/banners/upload-image
请求方式 :POST
请求数据类型 :application/x-www-form-urlencoded,application/json
响应数据类型 :*/*
接口描述:将图片文件上传到MinIO服务器,返回可访问的图片URL
响应参数:
json
{
"code": 200,
"message": "操作成功",
"data": "imgUrl访问地址"
}
4.7.2 功能实现
-
BannerController层
java@Autowired private BannerService bannerService; /** * 上传轮播图图片 * @param file 图片文件 * @return 图片访问URL */ @PostMapping("/upload-image") // 处理POST请求 @Operation(summary = "上传轮播图图片", description = "将图片文件上传到MinIO服务器,返回可访问的图片URL") // API描述 public Result<String> uploadBannerImage( @Parameter(description = "要上传的图片文件,支持jpg、png、gif等格式,大小限制5MB") @RequestParam("file") MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { String imgUrl = bannerService.uploadImage(file); return Result.success(imgUrl, "图片上传成功"); }
-
BannerService接口
java/** * 轮播图服务接口 */ public interface BannerService extends IService<Banner> { /** * 上传图片页面 * @param file * @return 返回图片 */ String uploadImage(MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException; } -
BannerServiceImpl实现类
java@Slf4j @Service public class BannerServiceImpl extends ServiceImpl<BannerMapper, Banner> implements BannerService { @Autowired private FileUploadService fileUploadService; /** * * 实现逻辑: * 核心校验 【1.文件非空校验 2.格式校验需要是image 3. 文件大小限制】 * 文件上传 * 上传图片页面 * @param file * @return 返回图片 */ @Override public String uploadImage(MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { //1. 非空校验 if (file.isEmpty()) { //配合全局异常处理,快速返回失败结果!! throw new RuntimeException("请选择要上传的文件!"); } //2. 图片格式校验 //获取文件的mimetype类型 String contentType = file.getContentType(); if (ObjectUtils.isEmpty(contentType) || !contentType.startsWith("image")) { //配合全局异常处理,快速返回失败结果!! throw new RuntimeException("轮播图只能上传图片文件!"); } //3. 文件大小限制 if (file.getSize() > 5 * 1024 * 1024) { //配合全局异常处理,快速返回失败结果!! throw new RuntimeException("图片文件大小不能超过5MB"); } //4. 调用文件上传业务 String imgUrl = fileUploadService.upload(file, "banners"); //5. 返回结果 log.info("完成banner图片上传,图片回显地址:{}",imgUrl); return imgUrl; } } -
测试
4.7.3 知识点
-
默认上传文件限制
默认servlet限制上传数据大小为10MB, 测试大图片会出现!
json{ "code": 500, "message": "Maximum upload size exceeded", "data": null }修改添加如何配置:
yamlspring: servlet: multipart: max-file-size: 100MB max-request-size: 150MB所以,可以省去代码中大小校验代码!
-
文件类型校验
MIME(Multipurpose Internet Mail Extensions,多用途互联网邮件扩展)是一种互联网标准,用字符串标记文件的「类型 + 格式」 。比如:
image/jpeg代表 JPEG 格式的图片video/mp4代表 MP4 格式的视频application/pdf代表 PDF 文档
它的核心作用是 让系统(浏览器、服务器、应用)快速识别文件本质内容 ,避免「后缀名造假」等问题(比如把
.exe改成.jpg,但 MIME 仍会暴露其真实类型)
java//获取文件的mimetype类型 String contentType = file.getContentType(); if (ObjectUtils.isEmpty(contentType) || !contentType.startsWith("image")) { //配合全局异常处理,快速返回失败结果!! throw new RuntimeException("轮播图只能上传图片文件!"); }
4.8 保存轮播图接口( post /api/banners/add)

4.8.1 接口分析
接口地址 :/api/banners/add
请求方式 :POST
请求数据类型 :application/x-www-form-urlencoded,application/json
响应数据类型 :*/*
接口描述:
创建新的轮播图,需要提供图片URL、标题、跳转链接等信息
json
{
"title": "智能考试系统介绍",
"description": "基于AI技术的智能考试平台,支持在线考试、智能组卷等功能",
"imageUrl": "https://example.com/images/banner1.jpg",
"linkUrl": "https://example.com/about",
"sortOrder": 1,
"isActive": true
}
响应参数:
json
{
"code": 200,
"message": "操作成功",
"data": ""
}