• SpringBoot 3.3.1 + Minio 实现极速上传和预览模式


    在这里插入图片描述

    统一版本管理

    <properties>
    	<minio.version>8.5.10</minio.version>
    	<aws.version>1.12.737</aws.version>
    	<hutool.version>5.8.28</hutool.version>
    </properties>
    
    <!--minio -->
    <dependency>
    	<groupId>io.minio</groupId>
    	<artifactId>minio</artifactId>
    	<version>${minio.version}</version>
    </dependency>
    <!--aws-s3-->
    <dependency>
    	<groupId>com.amazonaws</groupId>
    	<artifactId>aws-java-sdk-s3</artifactId>
    	<version>${aws.version}</version>
    </dependency>
    <!--hutool -->
    <dependency>
    	<groupId>cn.hutool</groupId>
    	<artifactId>hutool-all</artifactId>
    	<version>${hutool.version}</version>
    </dependency>
    
    

    项目配置 application-dev.yml

    # 文件系统
    minio: 
      #内部地址,可以访问到内网地址
      endpoint: http://172.16.11.110:10087
      access-key: xxxxxx
      secret-key: xxxxxx
      bucket-name: public-example-xxxx
      public-bucket-name: public-example-xxx
      #外网,互联网地址
      preview-domain: http://116.201.11.xxx:30087
    

    创建 MinioConfig

    package com.example.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import io.minio.MinioClient;
    import lombok.AllArgsConstructor;
    
    /**
     * aws-s3 通用存储操作 支持所有兼容s3协议的云存储: 阿里云OSS、腾讯云COS、华为云、七牛云、,京东云、minio 
     * @author weimeilayer@gmail.com
     * @date 2021年2月3日
     */
    @Configuration
    @AllArgsConstructor
    public class MinioConfig {
    	private final MinioProperties minioProperties;
    	
    	@Bean
    	public MinioClient minioClient() {
    		MinioClient minioClient = MinioClient.builder().endpoint(minioProperties.getEndpoint()).credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey()).build();
    		return minioClient;
    	}
    }
    

    创建 MinioProperties

    package com.example.config;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Configuration;
    import io.swagger.v3.oas.annotations.media.Schema;
    import lombok.Data;
    
    /**
     * aws 配置信息bucket 设置公共读权限
     * @author weimeilayer@gmail.com
     * @date 💓💕2021年4月1日🐬🐇 💓💕
     */
    @Data
    @Configuration
    @ConfigurationProperties(prefix = "minio")
    public class MinioProperties {
    
    	/**
    	 * 对象存储服务的URL
    	 */
    	@Schema(description = "对象存储服务的URL")
    	private String endpoint;
    
    	/**
    	 * 自定义域名
    	 */
    	@Schema(description = "自定义域名")
    	private String customDomain;
    
    	/**
    	 * 反向代理和S3默认支持
    	 */
    	@Schema(description = "反向代理和S3默认支持")
    	private Boolean pathStyleAccess = true;
    
    	/**
    	 * 应用ID
    	 */
    	@Schema(description = "应用ID")
    	private String appId;
    
    	/**
    	 * 区域
    	 */
    	@Schema(description = "区域")
    	private String region;
    	
    	/**
    	 * 预览地址
    	 */
    	@Schema(description = "预览地址")
    	private String previewDomain;
    
    	/**
    	 * Access key就像用户ID,可以唯一标识你的账户
    	 */
    	@Schema(description = "Access key就像用户ID,可以唯一标识你的账户")
    	private String accessKey;
    
    	/**
    	 * Secret key是你账户的密码
    	 */
    	@Schema(description = "Secret key是你账户的密码")
    	private String secretKey;
    
    	/**
    	 * 默认的存储桶名称
    	 */
    	@Schema(description = "默认的存储桶名称")
    	private String bucketName;
    	/**
         * 公开桶名
         */
    	@Schema(description = "公开桶名")
        private String publicBucketName;
    
        /**
         * 物理删除文件
         */
    	@Schema(description = "物理删除文件")
        private boolean physicsDelete;
        
    	/**
    	 * 最大线程数,默认: 100
    	 */
    	@Schema(description = "最大线程数,默认: 100")
    	private Integer maxConnections = 100;
    }
    
    

    创建 MinioTemplate

    package com.example.config;
    
    import java.io.ByteArrayInputStream;
    import java.io.InputStream;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.Calendar;
    import java.util.Date;
    import java.util.GregorianCalendar;
    import java.util.List;
    import java.util.Optional;
    
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.context.annotation.Configuration;
    
    import com.amazonaws.ClientConfiguration;
    import com.amazonaws.auth.AWSCredentials;
    import com.amazonaws.auth.AWSCredentialsProvider;
    import com.amazonaws.auth.AWSStaticCredentialsProvider;
    import com.amazonaws.auth.BasicAWSCredentials;
    import com.amazonaws.client.builder.AwsClientBuilder;
    import com.amazonaws.services.s3.AmazonS3;
    import com.amazonaws.services.s3.AmazonS3Client;
    import com.amazonaws.services.s3.model.Bucket;
    import com.amazonaws.services.s3.model.ObjectListing;
    import com.amazonaws.services.s3.model.ObjectMetadata;
    import com.amazonaws.services.s3.model.PutObjectResult;
    import com.amazonaws.services.s3.model.S3Object;
    import com.amazonaws.services.s3.model.S3ObjectSummary;
    import com.amazonaws.util.IOUtils;
    
    import lombok.RequiredArgsConstructor;
    import lombok.SneakyThrows;
    
    /**
     * aws-s3 通用存储操作 支持所有兼容s3协议的云存储: {阿里云OSS,腾讯云COS,七牛云,京东云,minio 等}
     * @author weimeilayer@gmail.com ✨
     * @date 💓💕2024年3月7日🐬🐇 💓💕
     */
    @Configuration
    @RequiredArgsConstructor
    public class MinioTemplate implements InitializingBean {
    	private final MinioProperties ossProperties;
    	private AmazonS3 amazonS3;
    
    	/**
    	 * 创建bucket
    	 * 
    	 * @param bucketName bucket名称
    	 */
    	@SneakyThrows
    	public void createBucket(String bucketName) {
    		if (!amazonS3.doesBucketExistV2(bucketName)) {
    			amazonS3.createBucket((bucketName));
    		}
    	}
    
    	/**
    	 * 获取全部bucket API Documentation</a>
    	 */
    	@SneakyThrows
    	public List<Bucket> getAllBuckets() {
    		return amazonS3.listBuckets();
    	}
    
    	/**
    	 * @param bucketName bucket名称 API Documentation</a>
    	 */
    	@SneakyThrows
    	public Optional<Bucket> getBucket(String bucketName) {
    		return amazonS3.listBuckets().stream().filter(b -> b.getName().equals(bucketName)).findFirst();
    	}
    
    	/**
    	 * @param bucketName bucket名称
    	 * @see <a href= Documentation</a>
    	 */
    	@SneakyThrows
    	public void removeBucket(String bucketName) {
    		amazonS3.deleteBucket(bucketName);
    	}
    
    	/**
    	 * 根据文件前置查询文件
    	 * 
    	 * @param bucketName bucket名称
    	 * @param prefix     前缀
    	 * @param recursive  是否递归查询
    	 * @return S3ObjectSummary 列表 API Documentation</a>
    	 */
    	@SneakyThrows
    	public List<S3ObjectSummary> getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {
    		ObjectListing objectListing = amazonS3.listObjects(bucketName, prefix);
    		return new ArrayList<>(objectListing.getObjectSummaries());
    	}
    
    	/**
    	 * 获取文件外链
    	 * 
    	 * @param bucketName bucket名称
    	 * @param objectName 文件名称
    	 * @param expires    过期时间 <=7
    	 * @return url
    	 */
    	@SneakyThrows
    	public String getObjectURL(String bucketName, String objectName, Integer expires) {
    		Date date = new Date();
    		Calendar calendar = new GregorianCalendar();
    		calendar.setTime(date);
    		calendar.add(Calendar.DAY_OF_MONTH, expires);
    		URL url = amazonS3.generatePresignedUrl(bucketName, objectName, calendar.getTime());
    		return url.toString();
    	}
    
    	/**
    	 * 获取文件
    	 * 
    	 * @param bucketName bucket名称
    	 * @param objectName 文件名称
    	 * @return 二进制流 API Documentation</a>
    	 */
    	@SneakyThrows
    	public S3Object getObject(String bucketName, String objectName) {
    		return amazonS3.getObject(bucketName, objectName);
    	}
    
    	/**
    	 * 上传文件
    	 * 
    	 * @param bucketName bucket名称
    	 * @param objectName 文件名称
    	 * @param stream     文件流
    	 * @throws Exception
    	 */
    	public void putObject(String bucketName, String objectName, InputStream stream) throws Exception {
    		putObject(bucketName, objectName, stream, (long) stream.available(), "application/octet-stream");
    	}
    
    	/**
    	 * 上传文件
    	 * 
    	 * @param bucketName  bucket名称
    	 * @param objectName  文件名称
    	 * @param stream      文件流
    	 * @param size        大小
    	 * @param contextType 类型
    	 * @throws Exception
    	 */
    	public PutObjectResult putObject(String bucketName, String objectName, InputStream stream, long size,
    			String contextType) throws Exception {
    		byte[] bytes = IOUtils.toByteArray(stream);
    		ObjectMetadata objectMetadata = new ObjectMetadata();
    		objectMetadata.setContentLength(size);
    		objectMetadata.setContentType(contextType);
    		ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
    		// 上传
    		return amazonS3.putObject(bucketName, objectName, byteArrayInputStream, objectMetadata);
    
    	}
    
    	/**
    	 * 获取文件信息
    	 * 
    	 * @param bucketName bucket名称
    	 * @param objectName 文件名称
    	 * @throws Exception API Documentation</a>
    	 */
    	public S3Object getObjectInfo(String bucketName, String objectName) throws Exception {
    		return amazonS3.getObject(bucketName, objectName);
    	}
    
    	/**
    	 * 删除文件
    	 * 
    	 * @param bucketName bucket名称
    	 * @param objectName 文件名称
    	 * @throws Exception
    	 */
    	public void removeObject(String bucketName, String objectName) throws Exception {
    		amazonS3.deleteObject(bucketName, objectName);
    	}
    
    	@Override
    	public void afterPropertiesSet() {
    		ClientConfiguration clientConfiguration = new ClientConfiguration();
    		clientConfiguration.setMaxConnections(ossProperties.getMaxConnections());
    		AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration(
    				ossProperties.getEndpoint(), ossProperties.getRegion());
    		AWSCredentials awsCredentials = new BasicAWSCredentials(ossProperties.getAccessKey(),
    				ossProperties.getSecretKey());
    		AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(awsCredentials);
    		this.amazonS3 = AmazonS3Client.builder().withEndpointConfiguration(endpointConfiguration)
    				.withClientConfiguration(clientConfiguration).withCredentials(awsCredentialsProvider)
    				.disableChunkedEncoding().withPathStyleAccessEnabled(ossProperties.getPathStyleAccess()).build();
    	}
    }
    
    

    创建Result

    package com.example.utils;
    
    import java.util.HashMap;
    
    /**
     * 响应信息主体
     * @author weimeilayer@gmail.com ✨
     * @date 💓💕2021年6月28日 🐬🐇 💓💕
     */
    public class Result extends HashMap<String, Object> {
    	private static final long serialVersionUID = 1L;
    
    	/** 状态码 */
    	public static final String CODE_TAG = "code";
    
    	/** 返回内容 */
    	public static final String MSG_TAG = "msg";
    
    	/** 数据对象 */
    	public static final String DATA_TAG = "data";
    
    	/**
    	 * 初始化一个新创建的 Result 对象,使其表示一个空消息。
    	 */
    	public Result() {
    	}
    
    	/**
    	 * 初始化一个新创建的 Result 对象
    	 *
    	 * @param code 状态码
    	 * @param msg  返回内容
    	 */
    	public Result(int code, String msg) {
    		super.put(CODE_TAG, code);
    		super.put(MSG_TAG, msg);
    	}
    
    	/**
    	 * 初始化一个新创建的 Result 对象
    	 *
    	 * @param code 状态码
    	 * @param msg  返回内容
    	 * @param data 数据对象
    	 */
    	public Result(int code, String msg, Object data) {
    		super.put(CODE_TAG, code);
    		super.put(MSG_TAG, msg);
    		if (data != null) {
    			super.put(DATA_TAG, data);
    		}
    	}
    
    	/**
    	 * 返回成功消息
    	 *
    	 * @return 成功消息
    	 */
    	public static Result success() {
    		return Result.success("操作成功");
    	}
    
    	/**
    	 * 返回成功数据
    	 *
    	 * @return 成功消息
    	 */
    	public static Result success(Object data) {
    		return Result.success("操作成功", data);
    	}
    
    	/**
    	 * 返回成功消息
    	 *
    	 * @param msg 返回内容
    	 * @return 成功消息
    	 */
    	public static Result success(String msg) {
    		return Result.success(msg, null);
    	}
    
    	/**
    	 * 返回成功消息
    	 *
    	 * @param msg  返回内容
    	 * @param data 数据对象
    	 * @return 成功消息
    	 */
    	public static Result success(String msg, Object data) {
    		return new Result(HttpStatus.SUCCESS, msg, data);
    	}
    
    	/**
    	 * 返回警告消息
    	 *
    	 * @param msg 返回内容
    	 * @return 警告消息
    	 */
    	public static Result warn(String msg) {
    		return Result.warn(msg, null);
    	}
    
    	/**
    	 * 返回警告消息
    	 *
    	 * @param msg  返回内容
    	 * @param data 数据对象
    	 * @return 警告消息
    	 */
    	public static Result warn(String msg, Object data) {
    		return new Result(HttpStatus.WARN, msg, data);
    	}
    
    	/**
    	 * 返回错误消息
    	 *
    	 * @return 错误消息
    	 */
    	public static Result error() {
    		return Result.error("操作失败");
    	}
    
    	/**
    	 * 返回错误消息
    	 *
    	 * @param msg 返回内容
    	 * @return 错误消息
    	 */
    	public static Result error(String msg) {
    		return Result.error(msg, null);
    	}
    
    	/**
    	 * 返回错误消息
    	 *
    	 * @param msg  返回内容
    	 * @param data 数据对象
    	 * @return 错误消息
    	 */
    	public static Result error(String msg, Object data) {
    		return new Result(HttpStatus.ERROR, msg, data);
    	}
    
    	/**
    	 * 返回错误消息
    	 *
    	 * @param code 状态码
    	 * @param msg  返回内容
    	 * @return 错误消息
    	 */
    	public static Result error(int code, String msg) {
    		return new Result(code, msg, null);
    	}
    
    	/**
    	 * 方便链式调用
    	 *
    	 * @param key   键
    	 * @param value 值
    	 * @return 数据对象
    	 */
    	@Override
    	public Result put(String key, Object value) {
    		super.put(key, value);
    		return this;
    	}
    }
    

    创建 HttpStatus

    package com.example.utils;
    
    /**
     * http请求状态
     * @author weimeilayer@gmail.com ✨
     * @date 💓💕2024年6月28日 🐬🐇 💓💕
     */
    public class HttpStatus {
    	/**
    	 * 操作成功
    	 */
    	public static final int SUCCESS = 200;
    
    	/**
    	 * 对象创建成功
    	 */
    	public static final int CREATED = 201;
    
    	/**
    	 * 请求已经被接受
    	 */
    	public static final int ACCEPTED = 202;
    
    	/**
    	 * 操作已经执行成功,但是没有返回数据
    	 */
    	public static final int NO_CONTENT = 204;
    
    	/**
    	 * 资源已被移除
    	 */
    	public static final int MOVED_PERM = 301;
    
    	/**
    	 * 重定向
    	 */
    	public static final int SEE_OTHER = 303;
    
    	/**
    	 * 资源没有被修改
    	 */
    	public static final int NOT_MODIFIED = 304;
    
    	/**
    	 * 参数列表错误(缺少,格式不匹配)
    	 */
    	public static final int BAD_REQUEST = 400;
    
    	/**
    	 * 未授权
    	 */
    	public static final int UNAUTHORIZED = 401;
    
    	/**
    	 * 访问受限,授权过期
    	 */
    	public static final int FORBIDDEN = 403;
    
    	/**
    	 * 资源,服务未找到
    	 */
    	public static final int NOT_FOUND = 404;
    
    	/**
    	 * 不允许的http方法
    	 */
    	public static final int BAD_METHOD = 405;
    
    	/**
    	 * 资源冲突,或者资源被锁
    	 */
    	public static final int CONFLICT = 409;
    
    	/**
    	 * 不支持的数据,媒体类型
    	 */
    	public static final int UNSUPPORTED_TYPE = 415;
    
    	/**
    	 * 系统内部错误
    	 */
    	public static final int ERROR = 500;
    
    	/**
    	 * 接口未实现
    	 */
    	public static final int NOT_IMPLEMENTED = 501;
    
    	/**
    	 * 系统警告消息
    	 */
    	public static final int WARN = 601;
    }
    
    

    创建 Constants

    package com.example.utils;
    
    /**
     * 通用常量信息
     * @author weimeilayer@gmail.com ✨
     * @date 💓💕2024年6月28日 🐬🐇 💓💕
     */
    public class Constants {
    	/**
    	 * UTF-8 字符集
    	 */
    	public static final String UTF8 = "UTF-8";
    
    	/**
    	 * GBK 字符集
    	 */
    	public static final String GBK = "GBK";
    
    	/**
    	 * www主域
    	 */
    	public static final String WWW = "www.";
    
    	/**
    	 * http请求
    	 */
    	public static final String HTTP = "http://";
    
    	/**
    	 * https请求
    	 */
    	public static final String HTTPS = "https://";
    
    	/**
    	 * 通用成功标识
    	 */
    	public static final String SUCCESS = "0";
    
    	/**
    	 * 通用失败标识
    	 */
    	public static final String FAIL = "1";
    
    	/**
    	 * 登录成功
    	 */
    	public static final String LOGIN_SUCCESS = "Success";
    
    	/**
    	 * 注销
    	 */
    	public static final String LOGOUT = "Logout";
    
    	/**
    	 * 注册
    	 */
    	public static final String REGISTER = "Register";
    
    	/**
    	 * 登录失败
    	 */
    	public static final String LOGIN_FAIL = "Error";
    
    	/**
    	 * 验证码有效期(分钟)
    	 */
    	public static final Integer CAPTCHA_EXPIRATION = 2;
    
    	/**
    	 * 令牌
    	 */
    	public static final String TOKEN = "token";
    
    	/**
    	 * 令牌前缀
    	 */
    	public static final String TOKEN_PREFIX = "Bearer ";
    
    	/**
    	 * 令牌前缀
    	 */
    	public static final String LOGIN_USER_KEY = "login_user_key";
    
    	/**
    	 * 用户头像
    	 */
    	public static final String JWT_AVATAR = "avatar";
    
    	/**
    	 * 创建时间
    	 */
    	public static final String JWT_CREATED = "created";
    
    	/**
    	 * 用户权限
    	 */
    	public static final String JWT_AUTHORITIES = "authorities";
    
    	/**
    	 * 资源映射路径 前缀
    	 */
    	public static final String RESOURCE_PREFIX = "/profile";
    
    	/**
    	 * RMI 远程方法调用
    	 */
    	public static final String LOOKUP_RMI = "rmi:";
    
    	/**
    	 * LDAP 远程方法调用
    	 */
    	public static final String LOOKUP_LDAP = "ldap:";
    
    	/**
    	 * LDAPS 远程方法调用
    	 */
    	public static final String LOOKUP_LDAPS = "ldaps:";
    }
    
    

    数据库表

    CREATE TABLE `sys_file` (
      `id` varchar(32) NOT NULL COMMENT '主键',
      `name` varchar(200) DEFAULT NULL COMMENT '原文件名',
      `group_id` varchar(32) DEFAULT NULL COMMENT '分组编号,对应多文件',
      `file_type` varchar(200) DEFAULT NULL COMMENT '文件类型',
      `suffix` varchar(200) DEFAULT NULL COMMENT '文件后缀',
      `size` int(11) DEFAULT NULL COMMENT '文件大小,单位字节',
      `preview_url` varchar(1000) DEFAULT NULL COMMENT '预览地址',
      `storage_type` varchar(200) DEFAULT NULL COMMENT '存储类型',
      `storage_url` varchar(200) DEFAULT NULL COMMENT '存储地址',
      `bucket_name` varchar(200) DEFAULT NULL COMMENT '桶名',
      `object_name` varchar(200) DEFAULT NULL COMMENT '桶内文件名',
      `visit_count` int(11) DEFAULT NULL COMMENT '访问次数',
      `sort` int(11) DEFAULT '0' COMMENT '排序值',
      `remarks` varchar(200) DEFAULT NULL COMMENT '备注',
      `gmt_create` timestamp NULL DEFAULT NULL COMMENT '创建时间',
      `gmt_modified` timestamp NULL DEFAULT NULL COMMENT '更新时间',
      `create_by` varchar(32) DEFAULT NULL COMMENT '创建人ID',
      `update_by` varchar(32) DEFAULT NULL COMMENT '修改人ID',
      `del_flag` varchar(32) DEFAULT '0' COMMENT '逻辑删除(0:未删除;null:已删除)',
      `tenant_id` int(11) DEFAULT NULL COMMENT '所属租户',
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='系统基本信息--文件管理信息';
    

    实体类 SysFile

    package com.example.entity;
    
    import java.io.Serial;
    import java.time.LocalDateTime;
    
    import com.baomidou.mybatisplus.annotation.FieldFill;
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableLogic;
    import com.baomidou.mybatisplus.annotation.TableName;
    import com.baomidou.mybatisplus.extension.activerecord.Model;
    import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
    
    import io.swagger.v3.oas.annotations.media.Schema;
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    
    /**
     * 系统基础信息--文件管理表
     * @author weimeilayer@gmail.com ✨
     * @date 💓💕2021年2月28日 🐬🐇 💓💕
     */
    @Data
    @TableName("sys_file")
    @EqualsAndHashCode(callSuper = false)
    @JsonIgnoreProperties(ignoreUnknown = true)
    @Schema(description = "系统基础信息--文件管理表")
    public class SysFile extends Model<SysFile> {
        @Serial
        private static final long serialVersionUID = 1L;
    	/**
         * 主键
         */
        @TableId(value = "id", type = IdType.ASSIGN_ID)
    	@Schema(description = "主键ID")
        private String id;
        /**
         * 原文件名
         */
        @Schema(description = "原文件名")
        private String name;
    	/**
    	 * 存储桶名称
    	 */
        @Schema(description = "原始文件名")
    	private String original;
        /**
         * 分组编号,用于对应多文件
         */
        @Schema(description = "分组编号,用于对应多文件")
        private String groupId;
        /**
         * 文件类型
         */
        @Schema(description = "文件类型")
        private String fileType;
        /**
         * 文件后缀
         */
        @Schema(description = "文件后缀")
        private String suffix;
        /**
         * 文件大小,单位字节
         */
        @Schema(description = "文件大小,单位字节")
        private Integer size;
        /**
         * 预览地址
         */
        @Schema(description = "预览地址")
        private String previewUrl;
        /**
         * 存储类型
         */
        @Schema(description = "存储类型")
        private String storageType;
        /**
         * 存储地址
         */
        @Schema(description = "存储地址")
        private String storageUrl;
        /**
         * 桶名
         */
        @Schema(description = "桶名")
        private String bucketName;
        /**
         * 桶内文件名
         */
        @Schema(description = "桶内文件名")
        private String objectName;
        /**
         * 访问次数
         */
        @Schema(description = "访问次数")
        private Integer visitCount;
        /**
         * 排序
         */
        @Schema(description = "排序")
        private Integer sort;
        /**
         * 备注
         */
        @Schema(description = "备注")
        private String remarks;
        /**
         * 逻辑删除(0:未删除;null:已删除)
         */
        @TableLogic
        @Schema(description = "逻辑删除(0:未删除;null:已删除)")
        @TableField(fill = FieldFill.INSERT)
        private String delFlag;
        /**
         * 创建人
         */
        @Schema(description = "创建人")
        @TableField(fill = FieldFill.INSERT)
        private String createBy;
        /**
         * 编辑人
         */
        @Schema(description = "编辑人")
        @TableField(fill = FieldFill.UPDATE)
        private String updateBy;
        /**
    	 * 创建时间
    	 */
    	@TableField(fill = FieldFill.INSERT)
    	@Schema(description = "创建时间")
    	private LocalDateTime gmtCreate;
    	/**
    	 * 编辑时间
    	 */
    	@Schema(description = "编辑时间")
    	@TableField(fill = FieldFill.UPDATE)
    	private LocalDateTime gmtModified;
    
    	/**
    	 * 所属租户
    	 */
    	@Schema(description = "所属租户")
    	private String tenantId;
    }
    

    创建接口类 SysFileService

    package com.example.service;
    
    import java.util.List;
    
    import org.springframework.web.multipart.MultipartFile;
    
    import com.baomidou.mybatisplus.core.metadata.IPage;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.example.dto.SysFileDto;
    import com.example.entity.SysFile;
    import com.example.utils.Result;
    import com.example.vo.SysFileSelVo;
    import com.example.vo.SysFileSortVo;
    
    import jakarta.servlet.http.HttpServletResponse;
    
    /**
     * 系统基础信息--文件管理服务类
     * @author weimeilayer@gmail.com ✨
     * @date 💓💕 2023年5月20日 🐬🐇 💓💕
     */
    public interface SysFileService extends IService<SysFile> {
    	/**
    	 * 上传文件
    	 * @param files
    	 * @param groupId
    	 * @param isPreview
    	 * @param isPublic
    	 * @param sort
    	 * @return
    	 */
    	Result uploadFile(MultipartFile[] files, String groupId, Boolean isPreview, Boolean isPublic, Integer sort);
        /**
         * 预览
         * @param groupId
         * @return
         */
    	Result preview(String groupId);
        /**
         * 分组预览
         * @param groupId
         * @param previewList
         * @return
         */
        boolean preview(String groupId, List<SysFileSelVo> previewList);
        /**
         * 下载
         * @param response
         * @param id
         */
        void download(HttpServletResponse response, String id);
        /**
         * 删除文件
         * @param id
         * @return
         */
        Result delete(String id);
        /**
         * 排序
         * @param vo
         * @return
         */
        Result sort(SysFileSortVo vo);
        
        /**
    	 * 分页查询SysFile
    	 * @param selvo 查询参数
    	 * @return
    	 */
    	public IPage<SysFileDto> getSysFileDtoPage(Page page,SysFileSelVo selvo);
    	/**
    	 * 上传文件
    	 * @param file
    	 * @return
    	 */
    	public Result uploadFile(MultipartFile file);
    
    	/**
    	 * 读取文件
    	 * @param bucket 桶名称
    	 * @param fileName 文件名称
    	 * @param response 输出流
    	 */
    	public void getFile(String bucket, String fileName, HttpServletResponse response);
    
    	/**
    	 * 删除文件
    	 * @param id
    	 * @return
    	 */
    	public Boolean deleteFile(String id);
    }
    

    实现类 SysFileServiceImpl

    package com.example.service.impl;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.URLEncoder;
    import java.nio.charset.StandardCharsets;
    import java.text.DecimalFormat;
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;
    
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    import org.springframework.util.StringUtils;
    import org.springframework.web.multipart.MultipartFile;
    
    import com.amazonaws.services.s3.model.S3Object;
    import com.baomidou.mybatisplus.core.metadata.IPage;
    import com.baomidou.mybatisplus.core.toolkit.Wrappers;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.example.config.MinioProperties;
    import com.example.config.MinioTemplate;
    import com.example.dto.SysFileDto;
    import com.example.dto.SysFileSelDto;
    import com.example.entity.SysFile;
    import com.example.mapper.SysFileMapper;
    import com.example.service.SysFileService;
    import com.example.utils.Result;
    import com.example.vo.SysFileSelVo;
    import com.example.vo.SysFileSortVo;
    
    import cn.hutool.core.collection.CollUtil;
    import cn.hutool.core.io.FileUtil;
    import cn.hutool.core.io.IoUtil;
    import cn.hutool.core.lang.Console;
    import cn.hutool.core.text.CharSequenceUtil;
    import cn.hutool.core.text.StrPool;
    import cn.hutool.core.util.IdUtil;
    import cn.hutool.core.util.StrUtil;
    import io.minio.GetObjectArgs;
    import io.minio.GetPresignedObjectUrlArgs;
    import io.minio.MinioClient;
    import io.minio.PutObjectArgs;
    import io.minio.RemoveObjectArgs;
    import io.minio.StatObjectArgs;
    import io.minio.StatObjectResponse;
    import io.minio.http.Method;
    import jakarta.servlet.http.HttpServletResponse;
    import lombok.AllArgsConstructor;
    import lombok.SneakyThrows;
    /**
     * 系统基础信息--文件管理服务实现类
     *
     * @author weimeilayer@gmail.com ✨
     * @date 💓💕 2023年5月20日 🐬🐇 💓💕
     */
    @Service
    @AllArgsConstructor
    public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile> implements SysFileService {
        private final MinioClient minioClient;
        private final MinioTemplate minioTemplate;
        private final MinioProperties minioProperties;
        /**
         * 上传文件
         *
         * @param file
         * @return
         */
        @Override
        public Result uploadFile(MultipartFile file) {
            String fileId = IdUtil.simpleUUID();
            String originalFilename = new String(Objects.requireNonNull(file.getOriginalFilename()).getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            String fileName = IdUtil.simpleUUID() + StrUtil.DOT + FileUtil.extName(originalFilename);
            Map<String, String> resultMap = new HashMap<>(4);
            resultMap.put("bucketName", minioProperties.getBucketName());
            resultMap.put("fileName", fileName);
            resultMap.put("originalFilename", originalFilename);
            resultMap.put("fileId", fileId);
            resultMap.put("url", String.format("/sysfile/%s/%s", minioProperties.getBucketName(), fileName));
            try (InputStream inputStream = file.getInputStream()) {
                minioTemplate.putObject(minioProperties.getBucketName(), fileName, inputStream, file.getSize(), file.getContentType());
                // 文件管理数据记录,收集管理追踪文件
                fileLog(file, fileName, fileId);
            } catch (Exception e) {
                log.error("上传失败", e);
                return Result.error(e.getLocalizedMessage());
            }
            return Result.success(resultMap);
        }
    
        /**
         * 读取文件
         *
         * @param bucket
         * @param fileName
         * @param response
         */
        @Override
        public void getFile(String bucket, String fileName, HttpServletResponse response) {
            try (S3Object s3Object = minioTemplate.getObject(bucket, fileName)) {
                response.setContentType("application/octet-stream; charset=UTF-8");
                IoUtil.copy(s3Object.getObjectContent(), response.getOutputStream());
            } catch (Exception e) {
                Console.log("文件读取异常: {}", e.getLocalizedMessage());
            }
        }
    
        /**
         * 删除文件
         *
         * @param id
         * @return
         */
        @Override
        @SneakyThrows
        @Transactional(rollbackFor = Exception.class)
        public Boolean deleteFile(String id) {
            SysFile file = this.getById(id);
            minioTemplate.removeObject(minioProperties.getBucketName(), file.getName());
            return file.updateById();
        }
        
        /**
         * 文件管理数据记录,收集管理追踪文件
         *
         * @param file     上传文件格式
         * @param fileName 文件名
         */
        private void fileLog(MultipartFile file, String fileName, String fileId) {
            String originalFilename = new String(Objects.requireNonNull(file.getOriginalFilename()).getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            SysFile sysFile = new SysFile();
            sysFile.setId(fileId);
            sysFile.setName(fileName);
            sysFile.setOriginal(originalFilename);
            sysFile.setSize((int) file.getSize());
            sysFile.setFileType(FileUtil.extName(file.getOriginalFilename()));
            sysFile.setBucketName(minioProperties.getBucketName());
            this.save(sysFile);
        }
        /**
         * 分页查询SysFile
         * @param page
         * @param selvo 查询参数
         * @return
         */
        @Override
        public IPage<SysFileDto> getSysFileDtoPage(Page page, SysFileSelVo selvo) {
            return baseMapper.getSysFileDtoPage(page, selvo);
        }
        @Override
        public Result uploadFile(MultipartFile[] files, String groupId, Boolean isPreview, Boolean isPublic, Integer sort) {
            if (files == null || files.length == 0) {
                return Result.error("上传文件不能为空!");
            }
            // 是否公开
            isPublic = isPublic != null && isPublic;
            // 是否预览
            isPreview = isPreview != null && isPreview;
            // 桶名
            String bucketName = isPublic ? minioProperties.getPublicBucketName() : minioProperties.getBucketName();
            // 文件目录
            String dir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd/"));
            // 预览列表
            List<SysFileSelDto> previewList = new ArrayList<>();
            // 分组编号,用于对应多文件
            if (StringUtils.hasText(groupId)) {
                // 排序
                if (sort == null) {
                    sort = baseMapper.getMaxSort(groupId);
                    if (sort != null) {
                        sort++;
                    } else {
                        sort = 0;
                    }
                }
            } else {
                groupId = IdUtil.simpleUUID();
                sort = 0;
            }
            for (int i = 0; i < files.length; i++) {
                MultipartFile file = files[i];
                InputStream in = null;
                try {
                    // 原文件名
                    String oriFileName = new String(file.getOriginalFilename().getBytes("ISO-8859-1"), "UTF-8");
                    // 后缀
                    String suffix = "";
                    if (StringUtils.hasText(oriFileName)) {
                        int index = oriFileName.lastIndexOf(StrPool.DOT);
                        if (index != -1) {
                            suffix = oriFileName.substring(index + 1);
                        }
                    }
                    // minio文件名
                    String objectName = dir + IdUtil.simpleUUID() + StrPool.DOT + suffix;
                    in = file.getInputStream();
                    // 上传文件
                    minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(in, file.getSize(), -1).contentType(file.getContentType()).build());
                    long size = file.getSize();
                    String id = IdUtil.simpleUUID();
                    String previewUrl = null;
                    if (isPreview) {
                        // 返回预览地址
                        previewUrl = getPreviewUrl(bucketName, objectName);
                        if (!StringUtils.hasText(previewUrl)) {
                            continue;
                        }
                        // 去掉后缀
                        if (isPublic) {
                            previewUrl = previewUrl.substring(0, previewUrl.indexOf("?"));
                        }
                        previewList.add(new SysFileSelDto(id, oriFileName, suffix, formatFileSize(size), previewUrl, i));
                    }
                    // minio文件信息插入数据库
                    minioInsertToDb(id, oriFileName, groupId, file.getContentType(), suffix, (int) size, bucketName, objectName, previewUrl, i + sort);
                } catch (Exception e) {
                    log.error(e.getMessage());
                    return Result.error("上传失败!");
                } finally {
                    if (in != null) {
                        try {
                            in.close();
                        } catch (IOException e) {
                            log.error(e.getMessage());
                        }
                    }
                }
            }
            return Result.success("上传成功!",isPreview ? previewList : groupId);
        }
    
        
        @Override
        public Result preview(String groupId) {
            List<SysFileSelVo> previewList = new ArrayList<>();
            boolean preview = preview(groupId, previewList);
            return preview ? Result.success(previewList) : Result.error("预览失败!");
        }
    
        /**
         * 文件下载
         */
        @Override
        public void download(HttpServletResponse response, String id) {
            SysFile sysFile = baseMapper.selectOne(Wrappers.<SysFile>lambdaQuery().select(SysFile::getBucketName, SysFile::getObjectName, SysFile::getName).eq(SysFile::getDelFlag, 0).eq(SysFile::getId, id));
            if (sysFile == null) {
                return;
            }
            String objectName = sysFile.getObjectName();
            if (CharSequenceUtil.isBlank(objectName)) {
                return;
            }
            InputStream in = null;
            try {
                String bucketName = sysFile.getBucketName();
                // 获取对象信息
                StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
                response.setContentType(stat.contentType());
                response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(sysFile.getName(), "UTF-8"));
                // 文件下载
                in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
                IoUtil.copy(in, response.getOutputStream());
            } catch (Exception e) {
                log.error(e.getMessage());
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        log.error(e.getMessage());
                    }
                }
            }
        }
    
        @Override
        public Result delete(String id) {
            SysFile sysFile = baseMapper.selectOne(Wrappers.<SysFile>lambdaQuery().select(SysFile::getId, SysFile::getBucketName, SysFile::getObjectName).eq(SysFile::getDelFlag, 0).eq(SysFile::getId, id));
            if (sysFile == null) {
                return Result.error("未找到文件!");
            }
            String objectName = sysFile.getObjectName();
            if (CharSequenceUtil.isBlank(objectName)) {
                return Result.error("未找到文件!");
            }
            // 数据库删除文件
            int update = baseMapper.update(null, Wrappers.<SysFile>lambdaUpdate().set(SysFile::getDelFlag, null).set(SysFile::getGmtModified, LocalDateTime.now()).set(SysFile::getUpdateBy, sysFile.getId()).eq(SysFile::getId, id));
            if (update == 0) {
            	Result.error("删除失败!");
            }
            // 是否物理删除minio上文件
            if (minioProperties.isPhysicsDelete()) {
                try {
                    minioClient.removeObject(RemoveObjectArgs.builder().bucket(sysFile.getBucketName()).object(objectName).build());
                    // minio文件信息数据库逻辑删除
                    minioDeleteToDb(objectName);
                } catch (Exception e) {
                    log.error(e.getMessage());
                    return Result.error("删除失败!");
                }
            }
            return Result.success("删除成功!");
        }
    
        @Override
        public Result sort(SysFileSortVo vo) {
            String id = vo.getId();
            Integer sort = vo.getSort();
            if (!StringUtils.hasText(id) || sort == null) {
                return Result.error("参数错误!");
            }
            SysFile sysFile = new SysFile();
            sysFile.setId(id);
            sysFile.setSort(sort);
            sysFile.updateById();
            return Result.success("编辑成功!");
        }
    
        /**
         * 文件大小处理
         *
         * @param fileSize 文件大小,单位B
         * @param fileSize
         * @return
         */
        private String formatFileSize(long fileSize) {
            DecimalFormat df = new DecimalFormat("#.00");
            String fileSizeizeString;
            String wrongSize = "0B";
            if (fileSize == 0) {
                return wrongSize;
            }
            if (fileSize < 1024) {
                fileSizeizeString = df.format((double) fileSize) + " B";
            } else if (fileSize < 1048576) {
                fileSizeizeString = df.format((double) fileSize / 1024) + " KB";
            } else if (fileSize < 1073741824) {
                fileSizeizeString = df.format((double) fileSize / 1048576) + " MB";
            } else {
                fileSizeizeString = df.format((double) fileSize / 1073741824) + " GB";
            }
            return fileSizeizeString;
        }
    
        /**
         * 获取预览地址路径
         *
         * @param bucketName 桶名
         * @param objectName minio文件名
         */
        private String getPreviewUrl(String bucketName, String objectName) {
            String previewUrl = null;
            try {
                // 预览地址
                previewUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(objectName)
                // 24小时,默认7天
                .expiry(60 * 60 * 24).expiry(15).build());
                if (StrUtil.isNotBlank(minioProperties.getPreviewDomain())) {
                    int count = 0;
                    int index = -1;
                    for (int i = 0; i < previewUrl.length(); i++) {
                        if (previewUrl.charAt(i) == '/') {
                            count++;
                            if (count == 3) {
                                index = i;
                                break;
                            }
                        }
                    }
                    if (index != -1) {
                        previewUrl = minioProperties.getPreviewDomain() + previewUrl.substring(index);
                    }
                }
            } catch (Exception e) {
                Console.log(e.getMessage());
            }
            return previewUrl;
        }
    
        /**
         * minio文件信息插入数据库
         *
         * @param id         主键
         * @param name       原文件名
         * @param groupId    分组编号,用于对应多文件
         * @param fileType   fileType
         * @param suffix     suffix
         * @param size       文件大小,单位字节
         * @param objectName 桶内文件名
         */
        private void minioInsertToDb(String id, String name, String groupId, String fileType, String suffix, Integer size, String bucketName, String objectName, String previewUrl, int sort) {
            SysFile sysFile = new SysFile();
            sysFile.setId(id);
            sysFile.setName(name);
            sysFile.setGroupId(groupId);
            sysFile.setFileType(fileType);
            sysFile.setSuffix(suffix);
            sysFile.setSize(size);
            sysFile.setStorageType("minio");
            sysFile.setBucketName(bucketName);
            sysFile.setObjectName(objectName);
            sysFile.setVisitCount(0);
            sysFile.setPreviewUrl(previewUrl);
            sysFile.setSort(sort);
            baseMapper.insert(sysFile);
        }
    
        /**
         * minio文件信息数据库逻辑删除
         *
         * @param objectName 桶内文件名
         */
        private void minioDeleteToDb(String objectName) {
            SysFile sysFile = baseMapper.selectOne(Wrappers.<SysFile>lambdaQuery().select(SysFile::getId).eq(SysFile::getObjectName, objectName).eq(SysFile::getDelFlag, 0));
            if (sysFile != null) {
                baseMapper.update(null, Wrappers.<SysFile>lambdaUpdate().set(SysFile::getDelFlag, null).set(SysFile::getGmtModified, LocalDateTime.now()).eq(SysFile::getId, sysFile.getDelFlag()));
            }
        }
    
        /**
         * 预览
         *
         * @param groupId 分组id
         */
        @Override
        public boolean preview(String groupId, List<SysFileSelVo> previewList) {
            List<SysFile> sysFiles = baseMapper.selectList(Wrappers.<SysFile>lambdaQuery().select(SysFile::getId, SysFile::getName, SysFile::getBucketName, SysFile::getObjectName, SysFile::getSuffix, SysFile::getSize, SysFile::getSort).eq(SysFile::getDelFlag, 0).eq(SysFile::getGroupId, groupId).orderByAsc(SysFile::getSort));
            if (CollUtil.isEmpty(sysFiles)) {
                return false;
            }
            for (SysFile sysFile : sysFiles) {
                try {
                    // 预览地址
                    String previewUrl = getPreviewUrl(sysFile.getBucketName(), sysFile.getObjectName());
                    // 文件大小并格式化
                    String size = formatFileSize(sysFile.getSize());
                    previewList.add(new SysFileSelVo(sysFile.getId(), sysFile.getName(), sysFile.getSuffix(), size, previewUrl, sysFile.getSort()));
                } catch (Exception e) {
                    Console.log(e.getMessage());
                }
            }
            return true;
        }
    }
    

    创建SysFileMapper

    package com.example.mapper;
    
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Param;
    
    import com.baomidou.mybatisplus.core.metadata.IPage;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.cqcloud.platform.common.data.datascope.DynamicBaseMapper;
    import com.cqcloud.platform.dto.SysFileDto;
    import com.cqcloud.platform.entity.SysFile;
    import com.cqcloud.platform.vo.SysFileSelVo;
    
    /**
     * 系统基础信息--文件管理信息 Mapper 接口
     * @author weimeilayer@gmail.com ✨
     * @date 💓💕 2021年5月20日 🐬🐇 💓💕
     */
    @Mapper
    public interface SysFileMapper extends BaseMapper<SysFile> {
    	/**
    	 * 排序
    	 * @param groupId
    	 * @return
    	 */
    	public Integer getMaxSort(@Param("groupId") String groupId);
    	/**
    	 * 分页查询SysFile
    	 * @param selvo 查询参数
    	 * @return
    	 */
    	public IPage<SysFileDto> getSysFileDtoPage(@Param("page")Page page,@Param("query")SysFileSelVo selvo);
    }
    
    

    创建 SysFileMapper.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.example.mapper.SysFileMapper">
    	<resultMap id="sysFileMap" type="com.example.dto.SysFileDto" >
    	        <result property="id" 				column="id"/>
    	        <result property="name" 			column="name"/>
    	        <result property="groupId" 			column="group_id"/>
    	        <result property="fileType" 		column="file_type"/>
    	        <result property="suffix" 			column="suffix"/>
    	        <result property="size" 			column="size"/>
    	        <result property="previewUrl" 		column="preview_url"/>
    	        <result property="storageType" 		column="storage_type"/>
    	        <result property="storageUrl" 		column="storage_url"/>
    	        <result property="bucketName" 		column="bucket_name"/>
    	        <result property="objectName" 		column="object_name"/>
    	        <result property="visitCount" 		column="visit_count"/>
    	        <result property="sort" 			column="sort"/>
    	        <result property="remarks" 			column="remarks"/>
    	        <result property="gmtCreate" 		column="gmt_create"/>
    	        <result property="gmtModified" 		column="gmt_modified"/>
    	        <result property="createBy" 		column="create_by"/>
    	        <result property="updateBy" 		column="update_by"/>
    	        <result property="delFlag" 			column="del_flag"/>
    	        <result property="tenantId" 		column="tenant_id"/>
    	    </resultMap>
    	<sql id="sysFileSql">
    t.id,t.name,t.group_id,t.file_type,t.suffix,t.size,t.preview_url,t.storage_type,t.storage_url,t.bucket_name,t.object_name,t.visit_count,t.sort,t.remarks,t.gmt_create,t.gmt_modified,t.create_by,t.update_by,t.del_flag,t.tenant_id
    	</sql>
    	<select id="getSysFileDtoPage" resultMap="sysFileMap">
    		select <include refid="sysFileSql" />
    		from sys_file t
    		<where>
    			t.del_flag='0'
    			<if test="query.name !=null and query.name !=''">
    				and t.name LIKE '%' || #{name} || '%'
    			</if>
    		</where>
    		order by t.gmt_create desc
    	</select>
    	<select id="getMaxSort" resultType="java.lang.Integer">
            select
            	max(sort)
            from
            	sys_file
            where
            	group_id = #{groupId}
            and del_flag = '0'
        </select>
    </mapper>
    

    创建 SysFileController

    package com.example.controller;
    
    import org.springdoc.core.annotations.ParameterObject;
    import org.springframework.http.HttpHeaders;
    import org.springframework.web.bind.annotation.DeleteMapping;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.example.common.log.annotation.SysLog;
    import com.example.service.SysFileService;
    import com.example.utils.Result;
    import com.example.vo.SysFileSelVo;
    import com.example.vo.SysFileSortVo;
    import io.swagger.v3.oas.annotations.Operation;
    import io.swagger.v3.oas.annotations.Parameter;
    import io.swagger.v3.oas.annotations.Parameters;
    import io.swagger.v3.oas.annotations.security.SecurityRequirement;
    import io.swagger.v3.oas.annotations.tags.Tag;
    import jakarta.servlet.http.HttpServletResponse;
    import lombok.AllArgsConstructor;
    
    /**
     * 系统基础信息--文件管理模块
     * @author weimeilayer@gmail.com
     * @date 2021-12-13 16:28:32
     */
    @RestController
    @AllArgsConstructor
    @RequestMapping("/sysfile")
    @SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
    public class SysFileController {
    
    	private final SysFileService sysFileService;
    	/**
    	 * 上传文件 文件名采用uuid,避免原始文件名中带"-"符号导致下载的时候解析出现异常
    	 * @param file 资源
    	 * @return R(/bucketName/filename)
    	 */
    	@PostMapping("/uploadOnToken")
    	public Result upload(@RequestParam("file") MultipartFile file) {
    		if (file.isEmpty()) {
    			return Result.error("文件上传失败");
    		}
    		return sysFileService.uploadFile(file);
    	}
    
    	/**
    	 * 获取文件
    	 * 
    	 * @param bucket   桶名称
    	 * @param fileName 文件空间/名称
    	 * @param response
    	 * @return
    	 */
    	@GetMapping("/{bucket}/{fileName}")
    	public void file(@PathVariable String bucket, @PathVariable String fileName, HttpServletResponse response) {
    		sysFileService.getFile(bucket, fileName, response);
    	}
    	
    	/**
    	 * 分页查询文件信息列表
    	 * @param page
    	 * @return
    	 */
    	@GetMapping("/pagelist")
    	public Result getSysFileDtoPage(@ParameterObject Page page,@ParameterObject SysFileSelVo selvo) {
    		return Result.success(sysFileService.getSysFileDtoPage(page, selvo));
    	}
    	
    	/**
    	 * 上传文件
    	 * @param file    多文件
    	 * @param groupId 分组id,用于文件追加
    	 */
    	@PostMapping("/upload")
    	@Parameters({
    		@Parameter(name = "groupId", description = "分组编号,用于对应多文件",example = "1"),
    		@Parameter(name = "isPreview", description = "是否预览", required = true,example = "1"),
    		@Parameter(name = "isPublic", description = "是否公开", required = true,example = "1"),
    		@Parameter(name = "sort", description = "排序", required = true,example = "1")})
    	public Result upload(@RequestParam MultipartFile[] file, String groupId, Boolean isPreview, Boolean isPublic,Integer sort) {
    		return sysFileService.uploadFile(file, groupId, isPreview, isPublic, sort);
    	}
    
    	/**
    	 * 批量预览文件
    	 * @param groupId 文件名
    	 */
    	@GetMapping("/preview/{groupId}")
    	public Result preview(@PathVariable("groupId") String groupId) {
    		return sysFileService.preview(groupId);
    	}
    
    	/**
    	 * 下载文件
    	 * @param id 主键
    	 */
    	@GetMapping("/download/{id}")
    	public void download(HttpServletResponse response, @PathVariable("id") String id) {
    		sysFileService.download(response, id);
    	}
    
    	/**
    	 * 删除文件
    	 * @param id 主键
    	 */
    	@DeleteMapping("/delete/{id}")
    	public Result delete(@PathVariable("id") String id) {
    		return sysFileService.delete(id);
    	}
    	/**
    	 * 文件排序
    	 * @param vo 排序封装
    	 */
    	@PostMapping("/sort")
    	public Result sort(@RequestBody SysFileSortVo vo) {
    		return sysFileService.sort(vo);
    	}
    }
    
  • 相关阅读:
    压力测试-Locust框架基本使用及更新报错解决方案
    Azure - 自动化机器学习AutoML Azure使用详解
    Spring AOP
    降噪耳机哪个牌子好又平价?降噪耳机性价比排行
    Rust星号(*)的作用-基础篇
    ElasticSearch(版本7.8.1)中类型Long精度缺失
    【线性系统理论】笔记二-状态转移矩阵+状态运动轨迹
    Flink实时写入Apache Doris如何保证高吞吐和低延迟
    JS防抖和节流在前端开发中的应用场景
    JDK对String操作优化
  • 原文地址:https://blog.csdn.net/weimeilayer/article/details/140042474