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

2023-10-27

写在最前

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

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

源码地址(前端):https://gitee.com/csps/mingyue-ui

文档地址:https://gitee.com/csps/mingyue/wikis

OSS 基础表设计

1. OSS对象存储表

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对象存储动态配置表

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配置

@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

@Slf4j
@Component
@RequiredArgsConstructor
public class OssApplicationRunner implements ApplicationRunner {

    private final SysOssConfigService sysOssConfigService;

    @Override
    public void run(ApplicationArguments args) {
        sysOssConfigService.init();
        log.info("初始化 OSS 配置成功");
    }

}

改进 OssFactory

@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 配置了

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

上传测试

{
  "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 上传信息保存

/**
 * 构建上传文件返回信息
 * @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服务对应的文件

@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);
}

删除接口

@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

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

删除后再打开

<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>

下载文件

逻辑实现

@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());
  }
}

下载接口

@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);
}

下载测试

curl -X 'GET' \
  'http://mingyue-gateway:9100/oss/sysOss/download/1701492631160229889' \
  -H 'accept: */*'

image-20230912190918686

小结

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

026-从零搭建微服务-文件服务(二) 的相关文章

随机推荐