diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssController.java index 81200c115..b3a76c214 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysOssController.java @@ -2,6 +2,8 @@ package org.dromara.system.controller.system; import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.annotation.SaIgnore; +import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.util.ObjectUtil; import org.dromara.common.core.domain.R; import org.dromara.common.core.validate.QueryGroup; @@ -18,9 +20,11 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.constraints.NotEmpty; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import java.io.IOException; import java.util.Arrays; @@ -92,6 +96,19 @@ public class SysOssController extends BaseController { ossService.download(ossId, response); } + /** + * 下载OSS对象(使用非阻塞流方式) + * + * @param ossId OSS对象ID + */ + @SaIgnore + @GetMapping("/streaming/download/{ossId}") + public ResponseEntity download(@PathVariable Long ossId) { + // 该方法是异步处理方法 SaToken 的权限注解不支持异步与同步混用,会报异常,但不影响正常的下载,此处手动校验即可 + StpUtil.checkPermission("system:oss:download"); + return ossService.streamingDownload(ossId); + } + /** * 删除OSS对象存储 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java index 057c068ce..55025133f 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java @@ -5,7 +5,9 @@ import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.system.domain.bo.SysOssBo; import org.dromara.system.domain.vo.SysOssVo; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.ResponseEntity; import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import java.io.File; import java.io.IOException; @@ -68,6 +70,13 @@ public interface ISysOssService { */ void download(Long ossId, HttpServletResponse response) throws IOException; + /** + * 非阻塞流下载方法 + * + * @param ossId OSS对象ID + */ + ResponseEntity streamingDownload(Long ossId); + /** * 删除OSS对象存储 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java index 33341a91d..4f11c31c6 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java @@ -21,6 +21,7 @@ import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.oss.core.OssClient; +import org.dromara.common.oss.core.WriteOutSubscriber; import org.dromara.common.oss.entity.UploadResult; import org.dromara.common.oss.enums.AccessPolicyType; import org.dromara.common.oss.factory.OssFactory; @@ -32,12 +33,16 @@ import org.dromara.system.mapper.SysOssMapper; import org.dromara.system.service.ISysOssService; import org.jetbrains.annotations.NotNull; import org.springframework.cache.annotation.Cacheable; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import java.io.File; import java.io.IOException; +import java.io.OutputStream; import java.time.Duration; import java.util.ArrayList; import java.util.Collection; @@ -183,6 +188,33 @@ public class SysOssServiceImpl implements ISysOssService, OssService { storage.download(sysOss.getFileName(), response.getOutputStream(), response::setContentLengthLong); } + /** + * 非阻塞流下载方法 + * + * @param ossId OSS对象ID + */ + @Override + public ResponseEntity streamingDownload(Long ossId) { + SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId); + if (ObjectUtil.isNull(sysOss)) { + throw new ServiceException("文件数据不存在!"); + } + OssClient storage = OssFactory.instance(sysOss.getService()); + + // 构建响应体 + String percentEncodedFileName = FileUtils.percentEncode(sysOss.getOriginalName()); + String contentDispositionValue = "attachment; filename=%s;filename*=utf-8''%s".formatted(percentEncodedFileName, percentEncodedFileName); + ResponseEntity.BodyBuilder bodyBuilder = ResponseEntity.ok() + .header(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "Content-Disposition,download-filename") + .header(HttpHeaders.CONTENT_DISPOSITION, contentDispositionValue) + .header("download-filename", percentEncodedFileName) + .contentType(MediaType.APPLICATION_OCTET_STREAM); + // 获取写出流订阅器 + WriteOutSubscriber subscriber = storage.download(sysOss.getFileName(), bodyBuilder::contentLength); + return bodyBuilder.body(subscriber::writeTo); + + } + /** * 上传 MultipartFile 到对象存储服务,并保存文件信息到数据库 * @@ -197,7 +229,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService { OssClient storage = OssFactory.instance(); UploadResult uploadResult; try { - uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType()); + uploadResult = storage.uploadSuffix(file.getInputStream(), suffix, file.getSize(), file.getContentType()); } catch (IOException e) { throw new ServiceException(e.getMessage()); }