026-从零搭建微服务-文件服务(二)

写在最前

如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。

源码地址(后端):gitee.com/csps/mingyu...

源码地址(前端):gitee.com/csps/mingyu...

文档地址:gitee.com/csps/mingyu...

OSS 基础表设计

1. OSS对象存储表

sql 复制代码
DROP TABLE IF EXISTS sys_oss;
CREATE TABLE sys_oss (
    oss_id           BIGINT(20)        NOT NULL                   COMMENT 'OSS对象ID',
    file_name        VARCHAR(255)      NOT NULL DEFAULT ''        COMMENT '文件名',
    original_name    VARCHAR(255)      NOT NULL DEFAULT ''        COMMENT '原名',
    file_suffix      VARCHAR(10)       NOT NULL DEFAULT ''        COMMENT '文件后缀名',
    file_url         VARCHAR(500)      NOT NULL                   COMMENT '文件URL',
    create_time      DATETIME          DEFAULT NULL               COMMENT '创建时间',
    create_by        VARCHAR(64)       DEFAULT ''                 COMMENT '上传人',
    update_time      DATETIME          DEFAULT NULl               COMMENT '更新时间',
    update_by        VARCHAR(64)       DEFAULT ''                 COMMENT '更新人',
    service          VARCHAR(20)       NOT NULL DEFAULT 'minio'   COMMENT '服务商',
    primary key (oss_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC COMMENT='OSS对象存储表';

2. OSS对象存储动态配置表

sql 复制代码
DROP TABLE IF EXISTS sys_oss_config;
CREATE TABLE sys_oss_config (
    oss_config_id     BIGINT(20)         NOT NULL                 COMMENT 'OSS动态配置ID',
    config_key        VARCHAR(20)        NOT NULL DEFAULT ''      COMMENT '配置key',
    access_key        VARCHAR(255)       DEFAULT ''               COMMENT 'accessKey',
    secret_key        VARCHAR(255)       DEFAULT ''               COMMENT '秘钥',
    bucket_name       VARCHAR(255)       DEFAULT ''               COMMENT '桶名称',
    prefix            VARCHAR(255)       DEFAULT ''               COMMENT '前缀',
    endpoint          VARCHAR(255)       DEFAULT ''               COMMENT '访问站点',
    domain            VARCHAR(255)       DEFAULT ''               COMMENT '自定义域名',
    is_https          CHAR(1)            DEFAULT 'N'              COMMENT '是否https(Y是 N否)',
    region            VARCHAR(255)       DEFAULT ''               COMMENT '域',
    access_policy     CHAR(1)            NOT NULL DEFAULT '1'     COMMENT '桶权限类型(0-private 1-public 2-custom)',
    status            CHAR(1)            DEFAULT '1'              COMMENT '是否默认(0是 1否)',
    extend            VARCHAR(255)       DEFAULT ''               COMMENT '扩展字段',
    create_by         VARCHAR(64)        DEFAULT ''               COMMENT '创建者',
    create_time       DATETIME           DEFAULT NULL             COMMENT '创建时间',
    update_by         VARCHAR(64)        DEFAULT ''               COMMENT '更新者',
    update_time       DATETIME           DEFAULT NULL             COMMENT '更新时间',
    PRIMARY KEY (oss_config_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC COMMENT='OSS对象存储动态配置表';
​
INSERT INTO `sys_oss_config` VALUES (1, 'minio', 'd6zVm5AP07uGCqSmsTxe', 'Vsm6qQDHgGchukEpyEoeX3dTe7fic60nTi8D9a0I', 'mingyue', '', 'mingyue-minio:5000', '', 'N', '', '1', '0', '', 'admin', '2023-09-11 17:50:40', 'admin', '2023-09-11 17:50:40');
COMMIT;

OSS 配置加载

初始化OSS配置

arduino 复制代码
@Override
public void init() {
    List<SysOssConfig> list = this.list();
    // 加载 OSS 初始化配置
    for (SysOssConfig config : list) {
        String configKey = config.getConfigKey();
        if ("0".equals(config.getStatus())) {
            RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, configKey);
        }
​
        RedisUtils.setCacheMapValue(OssConstant.SYS_OSS_CONFIG, config.getConfigKey(), JSONUtil.toJsonStr(config));
    }
}

OssApplicationRunner

less 复制代码
@Slf4j
@Component
@RequiredArgsConstructor
public class OssApplicationRunner implements ApplicationRunner {
​
    private final SysOssConfigService sysOssConfigService;
​
    @Override
    public void run(ApplicationArguments args) {
        sysOssConfigService.init();
        log.info("初始化 OSS 配置成功");
    }
​
}

改进 OssFactory

typescript 复制代码
@Slf4j
public class OssFactory {
​
  private static final Map<String, OssClient> CLIENT_CACHE = new ConcurrentHashMap<>();
​
  /**
   * 获取默认实例
   */
  public static OssClient instance() {
    // 获取redis 默认类型
    String configKey = RedisUtils.getCacheObject(OssConstant.DEFAULT_CONFIG_KEY);
    if (StrUtil.isEmpty(configKey)) {
      throw new OssException("文件存储服务类型无法找到!");
    }
    return instance(configKey);
  }
​
  /**
   * 根据类型获取实例
   */
  public static OssClient instance(String configKey) {
    String json = RedisUtils.getCacheMapValue(OssConstant.SYS_OSS_CONFIG, configKey);
    if (json == null) {
      throw new OssException("系统异常, '" + configKey + "'配置信息不存在!");
    }
    OssProperties properties = JSONUtil.toBean(json, OssProperties.class);
    OssClient client = CLIENT_CACHE.get(configKey);
    if (client == null) {
      CLIENT_CACHE.put(configKey, new OssClient(configKey, properties));
      log.info("创建OSS实例 key => {}", configKey);
      return CLIENT_CACHE.get(configKey);
    }
    // 配置不相同则重新构建
    if (!client.checkPropertiesSame(properties)) {
      CLIENT_CACHE.put(configKey, new OssClient(configKey, properties));
      log.info("重载OSS实例 key => {}", configKey);
      return CLIENT_CACHE.get(configKey);
    }
    return client;
  }
​
}

移除 Nacos OSS 配置

因为从数据库加载配置,所以不在需要 Nacos 配置了

yaml 复制代码
oss:
  configKey: minio
  endpoint: mingyue-minio:5000
  domain:
  prefix:
  accessKey: d6zVm5AP07uGCqSmsTxe
  secretKey: Vsm6qQDHgGchukEpyEoeX3dTe7fic60nTi8D9a0I
  bucketName: mingyue
  region: 
  isHttps: N
  accessPolicy: 1

上传测试

css 复制代码
{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "ossId": "1701490497677180930",
    "fileName": "2023-09-12/d1b5389a465f4bf7985844916d785c06.png",
    "originalName": "head_1.png",
    "fileSuffix": ".png",
    "fileUrl": "http://mingyue-minio:5000/mingyue/2023-09-12/d1b5389a465f4bf7985844916d785c06.png",
    "createTime": "2023-09-12 14:58:41",
    "createBy": "mingyue",
    "service": "minio"
  }
}

OSS 上传信息保存

typescript 复制代码
/**
 * 构建上传文件返回信息
 * @param originalFilename 原始文件名
 * @param suffix 文件后缀
 * @param configKey 配置key
 * @param uploadResult OSS服务返回结果
 * @return
 */
private SysOssVo buildResult(String originalFilename, String suffix, String configKey, UploadResult uploadResult) {
  SysOss oss = new SysOss();
  oss.setFileUrl(uploadResult.getFileUrl());
  oss.setFileSuffix(suffix);
  oss.setFileName(uploadResult.getFileName());
  oss.setOriginalName(originalFilename);
  oss.setService(configKey);
  this.save(oss);
​
  SysOssVo sysOssVo = BeanUtil.toBean(oss, SysOssVo.class);
  return this.matchingUrl(sysOssVo);
}

删除文件

逻辑实现

删除数据库记录的同时需要删除OSS服务对应的文件

kotlin 复制代码
@Override
public Boolean deleteByOssIds(List<Long> ossIds) {
  List<SysOss> list = this.listByIds(ossIds);
  if (CollUtil.isEmpty(list)) {
    return Boolean.FALSE;
  }
​
  for (SysOss sysOss : list) {
    OssClient storage = OssFactory.instance(sysOss.getService());
    storage.delete(sysOss.getFileUrl());
  }
​
  return this.removeBatchByIds(ossIds);
}

删除接口

less 复制代码
@DeleteMapping("/{ossIds}")
@Operation(summary = "删除OSS对象存储",
    parameters = { @Parameter(name = "ossIds", description = "oss对象Ids", required = true) })
public R<Boolean> remove(@NotEmpty(message = "主键不能为空") @PathVariable List<Long> ossIds) {
  return R.ok(sysOssService.deleteByOssIds(ossIds));
}

删除测试

删除前打开文件查看:http://mingyue-minio:5000/mingyue/2023-09-12/d1b5389a465f4bf7985844916d785c06.png

arduino 复制代码
curl -X 'DELETE' \
  'http://mingyue-gateway:9100/oss/sysOss/1701490497677180930' \
  -H 'accept: */*' \
  -H 'Authorization: 6H1mlA91zFRa5yEpIl2b2mnCjbG5B44f'

删除后再打开

javascript 复制代码
<Error>
    <Code>NoSuchKey</Code>
    <Message>The specified key does not exist.</Message>
    <Key>2023-09-12/d1b5389a465f4bf7985844916d785c06.png</Key>
    <BucketName>mingyue</BucketName>
    <Resource>/mingyue/2023-09-12/d1b5389a465f4bf7985844916d785c06.png</Resource>
    <RequestId>17841B7B6B41C214</RequestId>
    <HostId>dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8</HostId>
</Error>

下载文件

逻辑实现

java 复制代码
@Override
public void download(Long ossId, HttpServletResponse response) throws IOException {
  SysOss sysOss = this.getById(ossId);
  if (ObjectUtil.isNull(sysOss)) {
    throw new ServiceException("文件数据不存在!");
  }
  FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName());
  response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
  OssClient storage = OssFactory.instance(sysOss.getService());
​
  try (InputStream inputStream = storage.getObjectContent(sysOss.getFileUrl())) {
    int available = inputStream.available();
    IoUtil.copy(inputStream, response.getOutputStream(), available);
    response.setContentLength(available);
  }
  catch (Exception e) {
    throw new ServiceException(e.getMessage());
  }
}

下载接口

less 复制代码
@GetMapping("/download/{ossId}")
@Operation(summary = "下载OSS对象存储",
    parameters = { @Parameter(in = ParameterIn.PATH, name = "ossIds", description = "oss对象Ids", required = true) })
public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException {
  sysOssService.download(ossId, response);
}

下载测试

rust 复制代码
curl -X 'GET' \
  'http://mingyue-gateway:9100/oss/sysOss/download/1701492631160229889' \
  -H 'accept: */*'

小结

文件服务基础已经完成啦,接下来可以自己尝试集成其他厂商的 OSS 服务。

文件服务更新暂告一段落,接下来弄一弄搜索服务,打算用 ES(Elasticsearch)作为搜索服务基础工具,期待一下吧~~

相关推荐
LuckyLay8 分钟前
Spring学习笔记_27——@EnableLoadTimeWeaving
java·spring boot·spring
Stringzhua15 分钟前
【SpringCloud】Kafka消息中间件
spring·spring cloud·kafka
AskHarries1 小时前
Java字节码增强库ByteBuddy
java·后端
佳佳_1 小时前
Spring Boot 应用启动时打印配置类信息
spring boot·后端
程序媛小果2 小时前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot
许野平2 小时前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
BiteCode_咬一口代码3 小时前
信息泄露!默认密码的危害,记一次网络安全研究
后端
齐 飞4 小时前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb
狂放不羁霸4 小时前
idea | 搭建 SpringBoot 项目之配置 Maven
spring boot·maven·intellij-idea
LunarCod4 小时前
WorkFlow源码剖析——Communicator之TCPServer(中)
后端·workflow·c/c++·网络框架·源码剖析·高性能高并发