PBC-阿里云OSS实践
前言
文件上传,相信我们在大多数业务场景下都有这个需求。这个功能对业务系统来说也比较独立,和系统耦合度不高,所以完全可以利用组装式思想对其PBC化,使其能够被任何业务系统使用。
功能
- 文件上传
- 文件下载
- 文件详情
- 文件列表
- 文件删除
设计
我们选择阿里云的OSS服务进行文件存储,自己设计文件管理。
设计的关键是需要被任何业务系统使用,我们需要对各业务进行标识。通常情况下,业务一般具有自己的唯一标识,例如主键id等,所以我们通过下面几个参数就能唯一确定一个业务对象。
- 业务码
biz_code
- 业务唯一标识
biz_id
- 业务扩展 biz_content
我们设计一张表来存储文件信息
create table if not exists attachment
(
id bigint auto_increment comment '主键'
primary key,
gmt_create datetime not null comment '创建时间',
gmt_modified datetime null comment '修改时间',
file_key varchar(64) null comment '文件key',
file_type tinyint null comment '1文件、2图片',
biz_id bigint null comment '业务id',
biz_type tinyint default 0 not null comment '业务类型:0自检文件、1素材文件',
file_name varchar(128) default '' not null comment '文件名',
file_size bigint default 0 not null comment '文件大小 byte',
status tinyint default 2 null comment '0 删除 1 上传中 2 正常',
file_suffix varchar(32) null comment '文件类型后缀',
constraint uk_file_key
unique (file_key)
)
comment '附件' charset = utf8mb4;
实现
文件上传
利用阿里云OSS的能力,获取OSS的临时授权上传链接,业务系统拿到这个链接后,使用这个链接直接将文件上传了阿里云OSS服务器。
public FileUpdateResult uploadFile(String fileName, String bucketName) {
OSS ossClient = ossConfigProperties.getOssClient();
// 文件名生成
String suffix = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
String uuid = RandomStringUtils.randomAlphanumeric(32);
String fileKey = "oneyoung/" + uuid + "." + suffix;
// 生成签名URL。
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, fileKey, HttpMethod.PUT);
// 设置过期时间。
long expireEndTime = System.currentTimeMillis() + 60 * 60 * 1000;
Date expiration = new Date(expireEndTime);
request.setExpiration(expiration);
// 添加用户自定义元信息。
request.addUserMetadata("author", "oneyoung");
// 设置Content-Type。
Mimetypes instance = Mimetypes.getInstance();
String mimetype = instance.getMimetype(fileName);
request.setContentType(mimetype);
String url = "https://" + bucketName + "." + ossConfigProperties.getEndPoint() + "/" + fileKey;
// 通过HTTP PUT请求生成签名URL。
URL signedUrl = ossClient.generatePresignedUrl(request);
log.info("signed url for putObject: " + signedUrl);
// 添加PutObject请求头。
ossClient.shutdown();
return new FileUpdateResult(url, mimetype, signedUrl.toString(), fileKey, suffix);
}
public AppDataResult<Map<String, String>> getFileUploadInfos(PolymerizeAttachmentDTO request) {
String fileName = request.getFileName();
Map<String, String> result;
//插入附件信息表中
PolymerizeAttachmentDO polymerizeAttachmentDO = PolymerizeAttachmentDO.builder()
.bizId(request.getBizId())
.fileName(request.getFileName())
.fileSize(request.getFileSize())
.bizCode(request.getBizCode())
.fileType(request.getFileType())
.gmtCreate(LocalDateTime.now())
.status(AttachmentStatusEnum.UPLOAD_STATUS.getValue())
.build();
//如果上传的附件是视频,调用vod服务
if (FileTypeEnum.VIDEO_TYPE.getValue().equals(request.getFileType())) {
Long fileSize = request.getFileSize();
try {
result = appVodService.getUploadVideoInfos(fileName, fileSize);
String videoId = result.get("videoId");
polymerizeAttachmentDO.setFileKey(videoId);
polymerizeAttachmentDO.setFileSuffix(videoId);
} catch (Exception e) {
log.info("获取vod上传服务报错:" + e);
return AppDataResult.fail("获取vod上传服务报错");
}
} else {
//1.获取文件上传地址信息
FileUpdateResult fileUpdateResult = fileRepository.uploadFileToDefaultBucket(fileName);
//2.解析文件上传结果信息
result = fileRepository.getResultMap(fileUpdateResult);
polymerizeAttachmentDO.setFileKey(fileUpdateResult.getFileKey());
polymerizeAttachmentDO.setFileSuffix(fileUpdateResult.getSuffix());
}
polymerizeAttachmentMapper.insert(polymerizeAttachmentDO);
result.put("id", polymerizeAttachmentDO.getId().toString());
return AppDataResult.success(result);
}
由于我们的文件管理是自己控制,而文件上传最终是直接与OSS服务器进行对接,所以,我们系统并不知道文件是否真的上传成功,我们需要提供一个回调接口来确认文件是否上传成功。
public AppDataResult<Boolean> updateFileStatus(Long id) {
PolymerizeAttachmentDO polymerizeAttachmentDO = PolymerizeAttachmentDO.builder()
.id(id)
.status(AttachmentStatusEnum.NORMAL_STATUS.getValue())
.build();
polymerizeAttachmentMapper.updateByPrimaryKeySelective(polymerizeAttachmentDO);
return AppDataResult.success(true);
}
文件下载
通过biz_id、biz_code下载文件,获取到OSS的下载链接,业务系统直接下载
public AppDataResult<List<PolymerizeAttachmentDTO>> getFileDownInfos(Long bizId, String bizCode) {
PolymerizeAttachmentDO polymerizeAttachmentDO = PolymerizeAttachmentDO.builder()
.bizId(bizId)
.bizCode(bizCode)
.status(AttachmentStatusEnum.NORMAL_STATUS.getValue())
.build();
List<PolymerizeAttachmentDO> list = polymerizeAttachmentMapper.select(polymerizeAttachmentDO);
List<PolymerizeAttachmentDTO> result = dealPolymerizeAttachment(list);
return AppDataResult.success(result);
}
public String getFileDownloadUrl(String fileKey) {
if (StringUtils.isBlank(fileKey)) {
return null;
}
OSS ossClient = ossConfigProperties.getOssClient();
// 设置URL过期时间为1小时。
Date expiration = new Date(System.currentTimeMillis() + HOUR);
// 生成以GET方法访问的签名URL,访客可以直接通过浏览器访问相关内容。
URL url = ossClient.generatePresignedUrl(ossConfigProperties.getBucketName(), fileKey, expiration);
ossClient.shutdown();
return url.toString().replaceFirst("http", "https");
}