【项目实践】公寓租赁项目(四):基于SpringBoot文件管理接口开发

目录

一、上传文件接口开发

controller层:

创建MinioConfiguration类:

service层:

👍实现逻辑:

MultipartFile可以提供哪些方法获取上传文件的信息?

二、异常处理

👍解决方法:进行全局异常处理。


接口说明:FileUploadController

@Tag(name = "文件管理")
@RequestMapping("/admin/file")
@RestController
public class FileUploadController {

    @Autowired
    private FileService service;
}

一、上传文件接口开发

首先我们先理解一下上传文件的流程:


接口名称:upload

请求方式:Post

请求路径:/admin/file/upload

请求参数:@RequestParam MultipartFile file

返回类型:String

需要自定义方法“upload”,进行实现上传文件功能,最后返回文件url给前端。

controller层:
@Operation(summary = "上传文件")
    @PostMapping("upload")
    public Result<String> upload(@RequestParam MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
            String url=service.upload(file);
            return Result.ok(url);

    }

由于上传文件需要使用到MinIo,所以需要导入MinIo的依赖以及需要在"application.yml"配置MinIo。

依赖:

<dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
        </dependency>

配置文件:

minio:
  endpoint: http://192.168.10.101:9000
  access-key: minioadmin
  secret-key: minioadmin
  bucket-name: lease

在做完以上步骤之后我们需要创建一个MinIo的配置类,把MinioClient注入SoringIoc容器中,方便进行调用。

为了方便维护MinIo的配置信息,我们还需创建一个MinIo的配置信息的类(MinioProperties):

@Data
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {
    private String endpoint;
    private String accessKey;
    private String secretKey;
    private String bucketName;
}

@ConfigurationProperties注解:

标志这个类作为Configuration配置类的属性类,可以通过给"prefix"属性赋值,指定忽略配置文件的前缀。

创建MinioConfiguration类:
@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注解:

指定Minio的哪个配置属性类,如果没有这个注解,MinioProperties类会报错。

第二个注解@ConfigurationPropertiesScan("com.atguigu.lease.common.minio"):

有点类似@ComponentScan注解,通过指定包路径来映射配置属性类,这个注解适合拥有大量的配置属性类。


service层:
@Service
public class FileServiceImpl implements FileService {

    @Autowired
    private MinioProperties properties;

    @Autowired
    private MinioClient minioClient;

    @Override
    public String upload(MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {

        boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(properties.getBucketName()).build());
        if (!bucketExists) {
            minioClient
                    .makeBucket(MakeBucketArgs.builder()
                            .bucket(properties.getBucketName())
                            .build());
            minioClient
                    .setBucketPolicy(SetBucketPolicyArgs.builder()
                            .bucket(properties.getBucketName())
                            .config(createBucketPolicyConfig(properties.getBucketName()))
                            .build());
        }
        String filename = new SimpleDateFormat("yyyyMMdd").format(new Date()) +
                "/" + UUID.randomUUID() + "-" + file.getOriginalFilename();
        minioClient
                .putObject(PutObjectArgs.builder()
                        .bucket(properties.getBucketName())
                        .stream(file.getInputStream(), file.getSize(), -1)
                        .object(filename)
                        .contentType(file.getContentType())
                        .build());
        String url = String.join("/", properties.getEndpoint(), properties.getBucketName(), filename);
        return url;
    }

    private String createBucketPolicyConfig(String bucketName) {

        return """
                {
                  "Statement" : [ {
                    "Action" : "s3:GetObject",
                    "Effect" : "Allow",
                    "Principal" : "*",
                    "Resource" : "arn:aws:s3:::%s/*"
                  } ],
                  "Version" : "2012-10-17"
                }
                """.formatted(bucketName);
    }
}
👍实现逻辑:
  1. 注入先前配置好的MinioProperties和MinioClient。
  2. 判断MinIo中的"桶"是否已经存在,如果不存在则创建"桶"并且设置"桶"的权限(仅限自己可写入,所有人可以读)。
  3. 使用年月日+uuid+文件初始名字拼接起来作为文件名。
  4. 使用putObject方法(uploadObject方法只可以上传本地磁盘的文件)上传文件,其中需要使用MultipartFile的对象提供文件的属性信息进行设置。
  5. 拼接url:使用String类的join方法,把MinIo的端点、桶名字、文件名用"/"作为分隔符拼接起来。
  6. 返回url。
MultipartFile可以提供哪些方法获取上传文件的信息?
  • file.getSize():获取文件的大小
  • file.getOriginalFilename():获取文件的初始名字
  • file.getName():获取文件的上传后的名字
  • file.getInputStream():获取文件的输入流
  • file.getContentType():获取响应体中文件的类型

经测试,若不指定文件的ContentType,当访问文件的url时,此时浏览器会帮我们下载文件而不是访问文件内容。查看响应体,可知此时文件的ContentType为"application/octet-stream"的类型,这是一个流类型,而我们想要直接访问文件的内容,需要把ContentType设置为原来的类型,image/jpeg


二、异常处理

当我们在服务器里把Minio的服务关掉之后,上传图片仍然显示为"200",但是数据却返回的是null,显然跟我们的实现逻辑并不符合,很显然我们需要进行异常的处理。

关闭minio服务:

测试显示仍然响应成功,但是数据却为null:

👍解决方法:进行全局异常处理。

如果不进行全局异常处理,所有的接口方法都需要写try-catch来进行异常处理,显然并不是最优的解法。

全局异常处理类:GlobalExceptionHandler

导入依赖:

<!--spring-web-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result handle(Exception e){
        e.printStackTrace();
        return Result.fail();
    }
}

注解解释

  • @ControllerAdvice注解:用于声明处理全局Controller方法异常的类。
  • @ExceptionHandler注解:用于声明处理异常的方法,value属性用于声明该方法处理的异常类型 。
  • @ResponseBody注解:表示将方法的返回值作为HTTP的响应体。
    
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值