OssUploadDownloadFacadeImpl.java 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. package cc.uncarbon.module.oss.facade.impl;
  2. import cc.uncarbon.framework.core.exception.BusinessException;
  3. import cc.uncarbon.module.oss.enums.OssErrorEnum;
  4. import cc.uncarbon.module.oss.facade.OssUploadDownloadFacade;
  5. import cc.uncarbon.module.oss.model.request.UploadFileAttributeDTO;
  6. import cc.uncarbon.module.oss.model.response.OssFileDownloadReplyBO;
  7. import cc.uncarbon.module.oss.model.response.OssFileInfoBO;
  8. import cc.uncarbon.module.oss.service.OssFileInfoService;
  9. import cn.hutool.core.text.CharSequenceUtil;
  10. import cn.hutool.core.util.ObjectUtil;
  11. import lombok.NonNull;
  12. import lombok.RequiredArgsConstructor;
  13. import lombok.extern.slf4j.Slf4j;
  14. import org.dromara.x.file.storage.core.FileInfo;
  15. import org.dromara.x.file.storage.core.FileStorageService;
  16. import org.dromara.x.file.storage.core.constant.Constant;
  17. import org.dromara.x.file.storage.core.exception.FileStorageRuntimeException;
  18. import org.dromara.x.file.storage.core.upload.UploadPretreatment;
  19. import org.springframework.stereotype.Service;
  20. import java.net.URLEncoder;
  21. import java.nio.charset.StandardCharsets;
  22. import java.time.LocalDateTime;
  23. /**
  24. * 文件上传下载门面
  25. */
  26. @Slf4j
  27. @Service
  28. @RequiredArgsConstructor
  29. public class OssUploadDownloadFacadeImpl implements OssUploadDownloadFacade {
  30. private final OssFileInfoService ossFileInfoService;
  31. private final FileStorageService fileStorageService;
  32. @Override
  33. public OssFileInfoBO findByHash(String md5) {
  34. return ossFileInfoService.getOneByMd5(md5);
  35. }
  36. @Override
  37. public OssFileInfoBO upload(byte[] fileBytes, @NonNull UploadFileAttributeDTO attr) throws BusinessException {
  38. // 要上传到的平台名
  39. String platform = ObjectUtil.defaultIfNull(attr.getPlatform(), fileStorageService.getProperties()::getDefaultPlatform);
  40. // 如果需要缩略图: .setSaveThFilename().setThContentType()
  41. UploadPretreatment uploadPretreatment = fileStorageService
  42. .of(fileBytes)
  43. .setOriginalFilename(attr.getOriginalFilename())
  44. // 不手动指定,由框架自动生成存储文件名
  45. .setSaveFilename(null)
  46. .setContentType(attr.getContentType())
  47. .setPlatform(platform)
  48. .setPath(formatDatePath(LocalDateTime.now()));
  49. if (attr.isUseOriginalFilenameAsDownloadFileName() && fileStorageService.isSupportMetadata(platform)) {
  50. String downFileName = URLEncoder.encode(attr.getOriginalFilename(), StandardCharsets.UTF_8);
  51. uploadPretreatment.putMetadata(Constant.Metadata.CONTENT_DISPOSITION, "attachment;filename=" + downFileName);
  52. }
  53. FileInfo fileInfo;
  54. try {
  55. fileInfo = uploadPretreatment.upload();
  56. } catch (FileStorageRuntimeException fsre) {
  57. log.error("[文件上传下载门面][上传] FileStorageRuntimeException >> ", fsre);
  58. throw new BusinessException(OssErrorEnum.FILE_UPLOAD_FAILED);
  59. }
  60. log.info("[文件上传下载门面][上传] 正常上传成功 >> successFileInfo={}", fileInfo);
  61. /*
  62. 记录存入 DB
  63. */
  64. return ossFileInfoService.save(fileInfo, attr);
  65. }
  66. @Override
  67. public OssFileDownloadReplyBO downloadById(Long fileInfoId) throws BusinessException {
  68. /*
  69. 注:开启多租户后,因可能允许未登录下载,无法确定当前租户ID,导致查询 SQL 会错误地拼接 AND tenant_id = null 条件
  70. 解决方法1:Controller层方法解禁 @SaCheckLogin 注解,强制要求在 url-params 中传递 token 才可下载
  71. 解决方法2:后端使用 try-finally 临时设置特权租户上下文
  72. TenantContextHolder.setTenantContext(new TenantContext(HelioConstant.Tenant.DEFAULT_PRIVILEGED_TENANT_ID, "临时特权租户"))
  73. 以绕过行级租户过滤器;数据源级租户同理,使其固定访问某个数据源来查询文件信息
  74. 解决方法3:改造Controller层方法参数,增加传递一个租户ID字段,并在此切换上下文
  75. */
  76. OssFileInfoBO ossFileInfo = ossFileInfoService.getOneById(fileInfoId, true);
  77. /*
  78. 这里请根据实际业务性质调整
  79. 有的业务出于安全目的,不能暴露直链,只能通过服务端代理下载后,返回 byte[]
  80. 有的业务没有限制,上传后文件完全可以直接通过对象存储直链下载,如此还能节约服务端上传带宽
  81. 有的业务有安全要求,只能通过预签名地址下载
  82. 但本地存储又没有直链,只能通过文件ID;
  83. 默认地,此处按【本地存储or对象存储直链为空:通过服务端代理下载;对象存储:通过对象存储直链下载】返回
  84. */
  85. boolean redirect2DirectUrl = false;
  86. byte[] fileBytes = null;
  87. if (
  88. OssFileInfoService.isLocalPlatform(ossFileInfo.getStoragePlatform())
  89. || CharSequenceUtil.isEmpty(ossFileInfo.getDirectUrl())
  90. ) {
  91. FileInfo fileInfo = OssFileInfoService.toFileInfo(ossFileInfo);
  92. try {
  93. fileBytes = fileStorageService.download(fileInfo).bytes();
  94. } catch (FileStorageRuntimeException fsre) {
  95. log.error("[文件上传下载门面][下载] FileStorageRuntimeException >> ", fsre);
  96. throw new BusinessException(OssErrorEnum.FILE_DOWNLOAD_FAILED);
  97. }
  98. } else {
  99. /*if (fileStorageService.isSupportPresignedUrl(ossFileInfo.getStoragePlatform())) {
  100. // 采用预签名地址下载
  101. FileInfo fileInfo = OssFileInfoService.toFileInfo(ossFileInfo);
  102. DateTime oneHourLater = DateUtil.offsetHour(DateUtil.date(), 1);
  103. String preSignedUrl = fileStorageService.generatePresignedUrl(fileInfo, oneHourLater);
  104. ossFileInfo.setDirectUrl(preSignedUrl);*/
  105. redirect2DirectUrl = true;
  106. }
  107. return OssFileDownloadReplyBO.builder()
  108. .redirect2DirectUrl(redirect2DirectUrl)
  109. .fileBytes(fileBytes)
  110. .directUrl(ossFileInfo.getDirectUrl())
  111. .storageFilename(ossFileInfo.getStorageFilenameFull())
  112. .build();
  113. }
  114. @Override
  115. public boolean isLocalPlatform(String storagePlatform) {
  116. return OssFileInfoService.isLocalPlatform(storagePlatform);
  117. }
  118. /**
  119. * 格式化日期为路径形式,如:2024/01/01/
  120. * @param date 任意日期,一般取今日
  121. */
  122. private String formatDatePath(LocalDateTime date) {
  123. return String.format("%d/%02d/%02d/", date.getYear(), date.getMonthValue(), date.getDayOfMonth());
  124. }
  125. }