1.maven依赖
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.5.2</version>
</dependency>
2.application.yml
minio:
  url: http://localhost:9000
  accessKey: minioadmin
  secretKey: minio123
  bucketName: test
3.创建配置属性类MinioPropertes
@Component
// 这里的minio与上面的配置文件种的对应
@ConfigurationProperties(prefix = "minio")
public class MinioPropertes {
    /**
     * 外网访问服务地址
     */
    private String url;
    /**
     * 用户名
     */
    private String accessKey;
    /**
     * 密码
     */
    private String secretKey;
    /**
     * 桶名称
     */
    private String bucketName;
    /**
     * 预览到期时间(小时)
     */
    private Integer previewExpiry;
    public Integer getPreviewExpiry() {
        return previewExpiry;
    }
    public void setPreviewExpiry(Integer previewExpiry) {
        this.previewExpiry = previewExpiry;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getAccessKey() {
        return accessKey;
    }
    public void setAccessKey(String accessKey) {
        this.accessKey = accessKey;
    }
    public String getSecretKey() {
        return secretKey;
    }
    public void setSecretKey(String secretKey) {
        this.secretKey = secretKey;
    }
    public String getBucketName() {
        return bucketName;
    }
    public void setBucketName(String bucketName) {
        this.bucketName = bucketName;
    }
}
4.minio配置类
@Configuration
public class MinioConfig {
    @Autowired
    private MinioPropertes minioPropertes;
    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
                .endpoint(minioPropertes.getUrl())
                .credentials(minioPropertes.getAccessKey(), minioPropertes.getSecretKey())
                .build();
    }
}
5.写接口
public interface MinioService {
    /**
     * 上传文件
     * @param file
     */
    void uploadFile(MultipartFile file) throws Exception;
    
    void downloadFile(String filePath, HttpServletResponse response) throws Exception;
    String getPreviewUrl(String fileName);
}
6.接口实现
@Service
public class MinioServiceImpl implements MinioService{
    @Autowired
    private MinioPropertes minioPropertes;
    @Autowired
    private MinioClient minioClient;
    @Override
    public void uploadFile(MultipartFile file) throws Exception {
        // 存入minio
        minioClient.putObject(PutObjectArgs.builder()
                .bucket(minioPropertes.getBucketName())
                .object("2023/10/01/" + file.getOriginalFilename())
                .stream(file.getInputStream(), -1, 10485760)
                .contentType(file.getContentType())
                .build());
    }
    /**
     * 从minio下载文件,直接通过response传输
     */
    public void downloadFile(String filePath, HttpServletResponse response) throws Exception {
        try (InputStream is = getObject(minioPropertes.getBucketName(), filePath);
             BufferedInputStream bis = new BufferedInputStream(is);
             OutputStream os = response.getOutputStream()) {
            //try {
            response.setContentType("application/force-download;charset=utf-8");// 设置强制下载而不是直接打开
            response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(filePath, "UTF-8"));// 设置文件名
            byte[] buffer = new byte[1024 * 1024 * 1024]; //buffer 1M
            int offset = bis.read(buffer);
            while (offset != -1) {
                os.write(buffer, 0, offset);
                offset = bis.read(buffer);
            }
            os.flush();
        } catch (Exception e) {
            throw new RuntimeException("下载文件失败", e);
        }
    }
    @Override
    public String getPreviewUrl(String fileName) {
        if (StringUtils.isNotBlank(fileName)) {
            try {
                minioClient.statObject(StatObjectArgs.builder().bucket(minioPropertes.getBucketName()).object(fileName).build());
                if (null != minioPropertes.getPreviewExpiry()){
                    return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(minioPropertes.getBucketName()).object(fileName).expiry(minioPropertes.getPreviewExpiry(), TimeUnit.HOURS).build());
                }else {
                    return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(minioPropertes.getBucketName()).object(fileName).build());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    /**
     * 从bucket获取指定对象的输入流,后续可使用输入流读取对象
     * getObject与minio server连接默认保持5分钟,
     * 每隔15s由minio server向客户端发送keep-alive check,5分钟后由客户端主动发起关闭连接
     */
    public InputStream getObject(String bucketName, String objectName) throws Exception {
        GetObjectArgs args = GetObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build();
        return minioClient.getObject(args);
    }
}
7.控制器使用
@RestController
@RequestMapping("/file")
public class MinioController {
    @Autowired
    private MinioService minioService;
    @PostMapping("/upload")
    public String uploadFile(@RequestParam("file") MultipartFile multipartFile) throws Exception {
        minioService.uploadFile(multipartFile);
        return "ok";
    }
    /**
     * 文件下载请求
     */
    @GetMapping("downloadFile")
    public void downloadFile(String filePath, HttpServletResponse response) {
        try {
            minioService.downloadFile(filePath, response);
        } catch (Exception e) {
            System.out.println(e);
        }
    }
    /**
     * 文件下载请求
     */
    @GetMapping("preview")
    public String preview(String filePath, HttpServletResponse response) {
        try {
            return minioService.getPreviewUrl(filePath);
        } catch (Exception e) {
            System.out.println(e);
            return null;
        }
    }
}