SpringBoot 3 项目集成 MinIO

发布于:2025-05-22 ⋅ 阅读:(19) ⋅ 点赞:(0)

SpringBoot 3 项目集成 MinIO(正在浏览)

Vue 3 项目开发 MinIO 文件管理模块

1. 什么是 MinIO?

        MinIO 是一个高性能、分布式、云原生的对象存储系统,专为大规模数据存储和检索而设计。它兼容 Amazon S3 API,可以作为私有云或混合云环境中的存储解决方案,适用于现代数据密集型应用,如大数据分析、AI/ML、备份和归档等。

官网:https://www.minio.org.cn/

下载:https://dl.minio.org.cn/server/minio/release/windows-amd64/minio.exe

2. 启动 Windows 版本 MinIO

@echo off
minio.exe server D:\MinIO\data --address "localhost:9000" --console-address "localhost:9001"

3. MinioClient 常用 API

3.1 MinIO 中的 Bucket 和 Object

(1)Bucket(存储桶):是存储 Object 的逻辑空间(相当于顶层文件夹),每个Bucket之间的数据相互隔离。MinIO 兼容S3,S3 桶名只能包含小写字母、数字(0-9)、连字符(-)和点号(.),所以 Bucket 命名需满足 S3 桶名命名规则。

(2)Object(对象):是 MinIO 存储的对象(相当于文件)。

3.2 pom.xml 引入 MinIO 依赖

<!-- minio -->
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.5.17</version>
</dependency>

3.3 application.yml 自定义 MinIO 配置

spring:
  servlet:
    multipart:
      max-file-size: 20MB # 上传的最大文件大小

minio:
  enabled: true                     # 是否开启 minio 配置
  endpoint: http://127.0.0.1:9000   # MinIO API 地址
  accessKey: minioadmin             # 用户名
  secretKey: minioadmin             # 密码
  bucket: my-bucket                 # Bucket(存储桶)

3.4 MinIO 客户端配置

package com.dragon.springboot3vue3.config;

import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnProperty(prefix = "minio",name = "enabled", havingValue = "true")
public class MinioConfig {

    @Value("${minio.endpoint}")
    private String endpoint;
    @Value("${minio.accessKey}")
    private String accessKey;
    @Value("${minio.secretKey}")
    private String secretKey;

    /**
     * minio 客户端(这是单例Bean,但是线程安全)
     * @return
     */
    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey, secretKey)
                .build();
    }
}

3.5 MinioClient 常用 API

package com.dragon.springboot3vue3.controller;

import cn.dev33.satoken.util.SaResult;
import com.dragon.springboot3vue3.controller.dto.entityDto.MinioObjectDto;
import com.dragon.springboot3vue3.controller.dto.entityDto.MinioUploadObjectDto;
import com.dragon.springboot3vue3.utils.StringDTO;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Tag(name = "Minio接口")
@RestController
@RequestMapping("/minio")
public class MinioController {
    @Autowired
    private MinioClient minioClient;

    // 1. 操作 Bucket 存储桶 -----------------------------------------------------------------

    @Operation(summary = "创建 Bucket 存储桶")
    @PostMapping("/makeBucket")
    public SaResult makeBucket(@RequestBody StringDTO stringDTO) {
        try{
            // 判断是否存在 Bucket 存储桶
            boolean b = minioClient.bucketExists(BucketExistsArgs.builder()
                    .bucket(stringDTO.getStr())     // 存储桶名
                    .build());

            if(!b){
                minioClient.makeBucket(MakeBucketArgs.builder()
                        .bucket(stringDTO.getStr())     // 存储桶名
                        .build());

                return SaResult.ok();
            }
            return SaResult.error("Bucket 已存在,创建失败");
        } catch (Exception e) {
            return SaResult.error(e.getMessage());
        }

    }

    @Operation(summary = "获取 Bucket 存储桶列表")
    @GetMapping("/listBuckets")
    public SaResult listBuckets() {
        try{
            List<Bucket> buckets = minioClient.listBuckets();
            List<String> bucketNameList = buckets.stream().map(Bucket::name).toList();
            return SaResult.ok().setData(bucketNameList);
        } catch (Exception e) {
            return SaResult.error(e.getMessage());
        }
    }

    @Operation(summary = "删除 Bucket 存储桶")
    @GetMapping("/removeBuckets")
    public SaResult removeBuckets(@RequestBody StringDTO stringDTO) {
        try{
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(stringDTO.getStr())     // 存储桶名
                    .build());
            return SaResult.ok();
        } catch (Exception e) {
            return SaResult.error(e.getMessage());
        }
    }


    // 2. 操作 Object 对象 -----------------------------------------------------------------

    @Operation(summary = "上传 Object")
    @PostMapping("/uploadObject")
    public SaResult uploadObject(@RequestBody MinioUploadObjectDto minioUploadObjectDto) {
        try{
            if(StringUtils.isBlank(minioUploadObjectDto.getObjectName())){
                // 从文件路径中提取文件名(如 "D:\\Files\\bg1.jpg" → "bg1.jpg")
                String name = Paths.get(minioUploadObjectDto.getFilePath()).getFileName().toString();
                minioUploadObjectDto.setObjectName(name);
            }

            ObjectWriteResponse objectWriteResponse = minioClient.uploadObject(UploadObjectArgs.builder()
                    .bucket(minioUploadObjectDto.getBucketName())    // 存储桶名
                    .object(minioUploadObjectDto.getObjectName())    // 对象名
                    .filename(minioUploadObjectDto.getFilePath())    // 文件路径,如 "D:\\Files\\bg1.jpg"
                    .build());

            return SaResult.ok();
        } catch (Exception e) {
            return SaResult.error(e.getMessage());
        }
    }

    @Operation(summary = "获取 Object 状态信息")
    @PostMapping("/statObject")
    public SaResult statObject(@RequestBody MinioObjectDto minioObjectDto) {
        try{
            StatObjectResponse statObjectResponse = minioClient.statObject(StatObjectArgs.builder()
                    .bucket(minioObjectDto.getBucket())    // 存储桶名
                    .object(minioObjectDto.getObject())    // 对象名
                    .build());

            Map<String, Object> map = new HashMap<>();
            map.put("bucket", statObjectResponse.bucket());
            map.put("object", statObjectResponse.object());
            map.put("size", statObjectResponse.size());
            map.put("etag", statObjectResponse.etag());
            map.put("lastModified", statObjectResponse.lastModified());
            map.put("contentType", statObjectResponse.contentType());

            // 不能直接返回 statObjectResponse,是 MinIO 内部类,无法直接序列化为 JSON
            return SaResult.ok().setData(map);

        } catch (Exception e) {
            return SaResult.error(e.getMessage());
        }
    }

    /**
     * 
     * @param minioObjectDto
     * @return
     * @throws Exception
     */
    @Operation(summary = "获取访问 Object 的预签名 URL")
    @PostMapping("/getPresignedObjectUrl")
    public SaResult getPresignedObjectUrl(@RequestBody MinioObjectDto minioObjectDto) {
        try{
            String presignedObjectUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .bucket(minioObjectDto.getBucket())    // 存储桶名
                    .object(minioObjectDto.getObject())    // 对象名
                    .method(Method.GET)                    // URL 请求方法
//                    .expiry(5, TimeUnit.MINUTES)          // URL 过期时间
                    .build());
            return SaResult.ok().setData(presignedObjectUrl);
        } catch (Exception e) {
            return SaResult.error(e.getMessage());
        }
    }

    @Operation(summary = "获取所有 Object")
    @PostMapping("/listObjects")
    public SaResult listObjects(@RequestBody StringDTO stringDTO) {
        try{
            // 1. 获取存储桶中的对象列表
            Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
                    .bucket(stringDTO.getStr())    // 存储桶名
                    .build());

            // 2. 提取对象名到 List<String>
            List<String> list = new ArrayList<>();
            for (Result<Item> item : results) {
                list.add(item.get().objectName()); // 获取每个对象的名称
            }

            return SaResult.ok().setData(list);
        } catch (Exception e) {
            return SaResult.error(e.getMessage());
        }
    }

    @Operation(summary = "删除 Object")
    @PostMapping("/removeObject")
    public SaResult removeObject(@RequestBody MinioObjectDto minioObjectDto) {
        try{
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(minioObjectDto.getBucket())    // 存储桶名
                    .object(minioObjectDto.getObject())    // 对象名
                    .build());
            return SaResult.ok();
        } catch (Exception e) {
            return SaResult.error(e.getMessage());
        }
    }

}

4. SpringBoot 3 项目开发 MinIO Object 管理功能

4.1 前端页面展示

 ​

4.2 minio_object 表 SQL

CREATE TABLE `minio_object` (
  `id` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '主键',
  `user_id` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户ID',
  `bucket` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Bucket 桶名',
  `object` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Object 对象名',
  `name` varchar(255) DEFAULT NULL COMMENT '文件名',
  `url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'URL',
  `type` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '文件类型',
  `size` bigint DEFAULT NULL COMMENT '文件大小(B)',
  `md5` varchar(255) DEFAULT NULL COMMENT 'md5 文件唯一标识',
  `creator_id` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人ID',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `ts` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `delete_flag` int NOT NULL DEFAULT '0' COMMENT '逻辑删除字段(0:正常,1:删除)',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='MinIO Object表';

4.3 Java 代码

4.3.1 文件上传,object 命名和使用 md5 的作用

 4.3.2 文件下载(根据 ID 下载)

 4.3.3 文件下载(根据 URL 下载)

 4.3.4 使用 MyBatis-Plus 分页、连接查询

4.3.5 完整代码 

package com.dragon.springboot3vue3.controller;

import cn.dev33.satoken.util.SaResult;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.unit.DataSize;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dragon.springboot3vue3.common.Constant;
import com.dragon.springboot3vue3.controller.dto.entityDto.MinioObjectDto;
import com.dragon.springboot3vue3.controller.dto.pageDto.MinioObjectPageDto;
import com.dragon.springboot3vue3.entity.MinioObject;
import com.dragon.springboot3vue3.entity.User;
import com.dragon.springboot3vue3.service.IMinioObjectService;
import com.dragon.springboot3vue3.utils.StringDTO;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import io.minio.*;
import io.minio.http.Method;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedInputStream;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * <p>
 * MinIO Object表 前端控制器
 * </p>
 *
 * @author dragon
 * @since 2025-05-13
 */
@Tag(name = "Minio Object 接口")
@RestController
@RequestMapping("/minioObject")
public class MinioObjectController {
    @Autowired
    private IMinioObjectService minioObjectService;
    @Autowired
    private MinioClient minioClient;    // MinIO 客户端,主要用来调用 API

    @Value("${spring.servlet.multipart.max-file-size}")
    private String maxSize;             // yml 配置文件获取
    @Value("${minio.bucket}")
    private String minIOBucket;         // yml 配置文件获取


    @Transactional(rollbackFor = Exception.class)
    @Operation(summary = "文件上传")
    @PostMapping("/upload/{userId}")
    public SaResult upload(@RequestParam MultipartFile file,@PathVariable(value = "userId") String userId) {
        try {
            if(file.getSize() > DataSize.parse(maxSize).toBytes()){
                return SaResult.error("上传失败,上传文件过大");
            }

            // 文件大小(字节 B)
            long size = file.getSize();
            // 原文件名(bg.jpg)
            String originalFilename = file.getOriginalFilename();
            // 原文件类型(jpg)
            String type = FileUtil.extName(originalFilename);
            // 保证对象名唯一性(文件名由 uuid 拼接生成)
            String object = IdUtil.fastSimpleUUID() + StrUtil.DOT + type;

            // 生成文件唯一标识 md5,保证不会存储重复的文件
            String md5;
            try (BufferedInputStream bis = new BufferedInputStream(file.getInputStream())) {
                // 使用流计算 md5
                md5 = SecureUtil.md5(bis);
            }

            // 查找是否存在 md5 值,如果存在直接返回该对象
            MinioObject one = minioObjectService.lambdaQuery()
                    .eq(MinioObject::getMd5, md5)
                    .eq(MinioObject::getDeleteFlag, Constant.ZERO)
                    .one();
            if(Objects.nonNull(one)){
                return SaResult.ok().setData(one);
            }

            // 上传到 MinIO 服务器,object 对象名同名会被覆盖
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(minIOBucket)                 // 存储桶名
                    .object(object)                      // 对象名
                    .stream(file.getInputStream(),size,-1)   // 文件输入流,文件大小(字节),-1表示默认的分片大小(通常为5MB)
                    .build());

            // 获取 MinIO 中 Object Url
            String presignedObjectUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .bucket(minIOBucket)    // 存储桶名
                    .object(object)         // 对象名
                    .method(Method.GET)     // URL 请求方法
                    .build());

            // 保存到数据库
            MinioObject minioObject = new MinioObject();
            minioObject.setUserId(userId);
            minioObject.setBucket(minIOBucket);
            minioObject.setObject(object);
            minioObject.setName(originalFilename);
            minioObject.setUrl(presignedObjectUrl);
            minioObject.setType(type);
            minioObject.setSize(size);
            minioObject.setMd5(md5);
            minioObjectService.saveOrUpdate(minioObject);

            return SaResult.ok().setData(minioObject);
        }catch (Exception e) {
            return SaResult.error(e.getMessage());
        }
    }

    @Operation(summary = "wang-editor 富文本编辑器文件上传")
    @PostMapping("/wangEditorUpload/{userId}")
    public Map<String, Object> wangEditorUpload(@RequestParam MultipartFile file,@PathVariable(value = "userId") String userId) {
        try{
            if(file.getSize() > DataSize.parse(maxSize).toBytes()){
                return SaResult.error("上传失败,上传文件过大");
            }

            // 文件大小(字节)
            long size = file.getSize();
            // 原文件名
            String originalFilename = file.getOriginalFilename();
            // 原文件类型
            String type = FileUtil.extName(originalFilename);
            // 保证对象名唯一性(文件名由 uuid 拼接生成)
            String object = IdUtil.fastSimpleUUID() + StrUtil.DOT + type;

            // 生成文件唯一标识 md5,保证不会在磁盘存储重复的文件
            String md5;
            try (BufferedInputStream bis = new BufferedInputStream(file.getInputStream())) {
                // 使用流计算 md5
                md5 = SecureUtil.md5(bis);
            }

            // 存在 md5 值,直接返回该对象
            MinioObject one = minioObjectService.lambdaQuery()
                    .eq(MinioObject::getMd5, md5)
                    .eq(MinioObject::getDeleteFlag, Constant.ZERO)
                    .one();
            if(Objects.nonNull(one)){
                return SaResult.ok().setData(one);
            }

            // 上传到 MinIO 服务器,object 对象名同名会被覆盖
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(minIOBucket)                 // 存储桶名
                    .object(object)                      // 对象名
                    .stream(file.getInputStream(),size,-1)   // 文件输入流,文件大小(字节),-1表示默认的分片大小(通常为5MB)
                    .build());

            // 获取 Object Url
            String presignedObjectUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .bucket(minIOBucket)    // 存储桶名
                    .object(object)         // 对象名
                    .method(Method.GET)     // URL 请求方法
                    .build());

            // 保存到数据库
            MinioObject minioObject = new MinioObject();
            minioObject.setUserId(userId);
            minioObject.setBucket(minIOBucket);
            minioObject.setObject(object);
            minioObject.setName(originalFilename);
            minioObject.setUrl(presignedObjectUrl);
            minioObject.setType(type);
            minioObject.setSize(size);
            minioObject.setMd5(md5);
            minioObjectService.saveOrUpdate(minioObject);

            // 封装 wang-editor 数据返回格式
            Map<String, Object> map =new HashMap<>();
            map.put("errno",0);
            map.put("data", CollUtil.newArrayList(Dict.create().set("url",presignedObjectUrl)));

            return map;
        } catch (Exception e) {
            return SaResult.error(e.getMessage());
        }
    }

    /**
     *
     * @param id : MinioObject ID
     * @param response
     * @throws Exception
     */
    @Operation(summary = "根据 ID 文件下载")
    @GetMapping("/download/{id}")
    public void download(@PathVariable String id, HttpServletResponse response) throws Exception {
        MinioObject one = minioObjectService.lambdaQuery()
                .eq(MinioObject::getId, id)
                .eq(MinioObject::getDeleteFlag, Constant.ZERO)
                .one();

        if (one == null) {
            response.setStatus(404);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\": 404, \"msg\": \"文件不存在\"}");
            return;
        }

        String bucket = one.getBucket();
        String object = one.getObject();
        String name = one.getName();

        // 设置输出流格式
        // 添加 CORS 暴露请求头(必须!)
        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
        // 设置下载文件名
        String encodedName = URLEncoder.encode(name, StandardCharsets.UTF_8);
        response.addHeader("Content-Disposition","attachment;filename=" + encodedName);
        response.setContentType("application/octet-stream");

        try {
            // 从MinIO获取文件流
            GetObjectResponse getObjectResponse = minioClient.getObject(GetObjectArgs.builder()
                    .bucket(bucket)
                    .object(object)
                    .build());

            // 流式传输到HTTP响应
            getObjectResponse.transferTo(response.getOutputStream());
        } catch (Exception e) {
            response.setStatus(404);
        }
        response.getOutputStream().flush();
    }

    @Operation(summary = "根据 URL 文件下载")
    @PostMapping("/downloadByUrl")
    public void downloadByUrl(@RequestBody StringDTO stringDTO, HttpServletResponse response) throws Exception {
        // 解密 url
        String decodedUrl = URLDecoder.decode(stringDTO.getStr(), StandardCharsets.UTF_8);

        MinioObject one = minioObjectService.lambdaQuery()
                .eq(MinioObject::getUrl, decodedUrl)
                .eq(MinioObject::getDeleteFlag, Constant.ZERO)
                .one();

        if (one == null) {
            response.setStatus(404);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\": 404, \"msg\": \"文件不存在\"}");
            return;
        }

        String bucket = one.getBucket();
        String object = one.getObject();
        String name = one.getName();

        // 设置输出流格式
        // 添加 CORS 暴露请求头(必须!)
        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
        // 设置下载文件名
        String encodedName = URLEncoder.encode(name, StandardCharsets.UTF_8);
        response.addHeader("Content-Disposition","attachment;filename=" + encodedName);
        response.setContentType("application/octet-stream");

        try {
            // 从MinIO获取文件流
            GetObjectResponse getObjectResponse = minioClient.getObject(GetObjectArgs.builder()
                    .bucket(bucket)
                    .object(object)
                    .build());

            // 流式传输到HTTP响应
            getObjectResponse.transferTo(response.getOutputStream());
        } catch (Exception e) {
            response.setStatus(404);
        }
        response.getOutputStream().flush();
    }

    @Operation(summary = "分页列表")
    @PostMapping("/list")
    public SaResult list(@RequestBody MinioObjectPageDto pageDto){
        // 创建分页对象
        Page<MinioObjectDto> page=new Page<>(pageDto.getCurrentPage(), pageDto.getPageSize());

        // 构造询条件
        MPJLambdaWrapper<MinioObject> qw = new MPJLambdaWrapper<MinioObject>()
                .selectAll(MinioObject.class)
                .selectAs(User::getName, MinioObjectDto::getUserName)
                .leftJoin(User.class, User::getId, MinioObject::getUserId)
                .like(StringUtils.isNotBlank(pageDto.getName()), MinioObject::getName, pageDto.getName())
                .like(StringUtils.isNotBlank(pageDto.getBucket()), MinioObject::getBucket, pageDto.getBucket())
                .like(StringUtils.isNotBlank(pageDto.getType()), MinioObject::getType, pageDto.getType())
                .orderByDesc(MinioObject::getCreateTime);

        // 根据查询条件,将结果封装到分页对象
        Page<MinioObjectDto> response = minioObjectService.selectJoinListPage(page, MinioObjectDto.class, qw);

        return SaResult.ok().setData(response);
    }

    @Transactional(rollbackFor = Exception.class)
    @Operation(summary = "删除")
    @DeleteMapping("/remove")
    public SaResult remove(@RequestBody @Validated StringDTO stringDTO){
        MinioObject object = minioObjectService.lambdaQuery().eq(MinioObject::getId, stringDTO.getStr()).one();

        try {
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(minIOBucket)            // 存储桶名
                    .object(object.getObject())     // 对象名
                    .build());
        } catch (Exception e) {
            return SaResult.error(e.getMessage());
        }

        minioObjectService.removeById(stringDTO.getStr());
        return SaResult.ok();
    }

}