写在最前
如果这个项目让你有所收获,记得 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;@Overridepublic 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: minioendpoint: mingyue-minio:5000domain:prefix:accessKey: d6zVm5AP07uGCqSmsTxesecretKey: Vsm6qQDHgGchukEpyEoeX3dTe7fic60nTi8D9a0IbucketName: mingyueregion: isHttps: NaccessPolicy: 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: */*'
小结
文件服务基础已经完成啦,接下来可以自己尝试集成其他厂商的 OSS 服务。
文件服务更新暂告一段落,接下来弄一弄搜索服务,打算用 ES(Elasticsearch)作为搜索服务基础工具,期待一下吧~~