diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/FilterChainUtils.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/FilterChainUtils.java index 17d16f53c3..e92891eb9a 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/FilterChainUtils.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/FilterChainUtils.java @@ -34,6 +34,8 @@ public class FilterChainUtils { filterChainDefinitionMap.put("/attachment/download/file/**", "anon"); //用例评审副文本访问 filterChainDefinitionMap.put("/review/functional/case/download/file/**", "anon"); + //缺陷管理富文本访问 + filterChainDefinitionMap.put("/bug/attachment/preview/md/**", "anon"); filterChainDefinitionMap.put("/system/version/current", "anon"); diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugAttachmentController.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugAttachmentController.java index 0c3478c167..d2f566e73b 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugAttachmentController.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugAttachmentController.java @@ -133,6 +133,7 @@ public class BugAttachmentController { return bugAttachmentService.upgrade(request, SessionUtils.getUserId()); } + // 富文本相关接口 @PostMapping("/upload/md/file") @Operation(summary = "缺陷管理-富文本附件-上传") @RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.PROJECT_BUG_ADD, PermissionConstants.PROJECT_BUG_UPDATE}) @@ -140,11 +141,9 @@ public class BugAttachmentController { return bugAttachmentService.uploadMdFile(file); } - @PostMapping(value = "/preview/md/compressed") + @GetMapping(value = "/preview/md/{projectId}/{fileId}/{compressed}") @Operation(summary = "缺陷管理-富文本缩略图-预览") - @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ) - @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") - public ResponseEntity previewMdImg(@Validated @RequestBody BugFileSourceRequest request) { - return bugAttachmentService.downloadOrPreview(request); + public ResponseEntity previewMd(@PathVariable String projectId, @PathVariable String fileId, @PathVariable("compressed") boolean compressed) { + return bugAttachmentService.previewMd(projectId, fileId, compressed); } } diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugController.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugController.java index 3337521532..f1b5ca67bd 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugController.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugController.java @@ -9,7 +9,10 @@ import io.metersphere.bug.dto.request.*; import io.metersphere.bug.dto.response.BugColumnsOptionDTO; import io.metersphere.bug.dto.response.BugDTO; import io.metersphere.bug.dto.response.BugDetailDTO; -import io.metersphere.bug.service.*; +import io.metersphere.bug.service.BugLogService; +import io.metersphere.bug.service.BugNoticeService; +import io.metersphere.bug.service.BugService; +import io.metersphere.bug.service.BugSyncService; import io.metersphere.project.dto.ProjectTemplateOptionDTO; import io.metersphere.project.service.ProjectApplicationService; import io.metersphere.project.service.ProjectTemplateService; @@ -53,8 +56,6 @@ public class BugController { @Resource private BugSyncService bugSyncService; @Resource - private BugStatusService bugStatusService; - @Resource private ProjectTemplateService projectTemplateService; @Resource private ProjectApplicationService projectApplicationService; diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugAttachmentService.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugAttachmentService.java index 2d78fb1936..ed15f63885 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugAttachmentService.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugAttachmentService.java @@ -261,10 +261,17 @@ public class BugAttachmentService { String fileId = IDGenerator.nextStr(); FileRequest fileRequest = new FileRequest(); fileRequest.setFileName(file.getOriginalFilename()); - String systemTempDir = DefaultRepositoryDir.getSystemTempDir(); - fileRequest.setFolder(systemTempDir + "/" + fileId); + fileRequest.setFolder(DefaultRepositoryDir.getSystemTempDir() + "/" + fileId); try { FileCenter.getDefaultRepository().saveFile(file, fileRequest); + String fileType = StringUtils.substring(fileName, fileName.lastIndexOf(".") + 1); + if (TempFileUtils.isImage(fileType)) { + //图片文件自动生成预览图 + byte[] previewImg = TempFileUtils.compressPic(file.getBytes()); + fileRequest.setFolder(DefaultRepositoryDir.getSystemTempCompressDir() + "/" + fileId); + fileRequest.setStorage(StorageType.MINIO.toString()); + fileService.upload(previewImg, fileRequest); + } } catch (Exception e) { LogUtils.error(e); throw new MSException(e.getMessage()); @@ -625,7 +632,7 @@ public class BugAttachmentService { * @param source 文件来源 */ public void transferTmpFile(String bugId, String projectId, List uploadFileIds, String userId, String source) { - if (org.apache.commons.collections.CollectionUtils.isEmpty(uploadFileIds)) { + if (CollectionUtils.isEmpty(uploadFileIds)) { return; } //过滤已上传过的 @@ -741,4 +748,85 @@ public class BugAttachmentService { } } } + + public ResponseEntity previewMd(String projectId, String fileId, boolean compressed) { + byte[] bytes; + String fileName; + BugLocalAttachmentExample example = new BugLocalAttachmentExample(); + example.createCriteria().andFileIdEqualTo(fileId); + List bugAttachments = bugLocalAttachmentMapper.selectByExample(example); + if (CollectionUtils.isEmpty(bugAttachments)) { + //在临时文件获取 + fileName = getTempFileNameByFileId(fileId); + bytes = getPreviewImg(fileName, fileId, compressed); + } else { + //在正式目录获取 + BugLocalAttachment attachment = bugAttachments.get(0); + fileName = attachment.getFileName(); + FileRequest fileRequest = new FileRequest(); + fileRequest.setFileName(attachment.getFileName()); + if (compressed) { + fileRequest.setFolder(DefaultRepositoryDir.getBugPreviewDir(projectId, attachment.getBugId()) + "/" + attachment.getFileId()); + } else { + fileRequest.setFolder(DefaultRepositoryDir.getBugDir(projectId, attachment.getBugId()) + "/" + attachment.getFileId()); + } + fileRequest.setStorage(StorageType.MINIO.name()); + try { + bytes = fileService.download(fileRequest); + } catch (Exception e) { + throw new MSException("get file error"); + } + } + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType("application/octet-stream")) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"") + .body(bytes); + } + + public byte[] getPreviewImg(String fileName, String fileId, boolean isCompressed) { + String systemTempDir; + if (isCompressed) { + systemTempDir = DefaultRepositoryDir.getSystemTempCompressDir(); + } else { + systemTempDir = DefaultRepositoryDir.getSystemTempDir(); + } + FileRequest previewRequest = new FileRequest(); + previewRequest.setFileName(fileName); + previewRequest.setStorage(StorageType.MINIO.name()); + previewRequest.setFolder(systemTempDir + "/" + fileId); + byte[] previewImg = null; + try { + previewImg = fileService.download(previewRequest); + } catch (Exception e) { + LogUtils.error("获取预览图失败:{}", e); + } + + if (previewImg == null || previewImg.length == 0) { + try { + if (isCompressed) { + previewImg = this.compressPicWithFileMetadata(fileName, fileId); + previewRequest.setFolder(DefaultRepositoryDir.getSystemTempCompressDir() + "/" + fileId); + fileService.upload(previewImg, previewRequest); + } + return previewImg; + } catch (Exception e) { + LogUtils.error("获取预览图失败:{}", e); + } + } + return previewImg; + } + + //获取文件并压缩的方法需要上锁,防止并发超过一定数量时内存溢出 + private synchronized byte[] compressPicWithFileMetadata(String fileName, String fileId) throws Exception { + byte[] fileBytes = this.getFile(fileName, fileId); + return TempFileUtils.compressPic(fileBytes); + } + + public byte[] getFile(String fileName, String fileId) throws Exception { + FileRequest fileRequest = new FileRequest(); + fileRequest.setFileName(fileName); + fileRequest.setFolder(DefaultRepositoryDir.getSystemTempDir() + "/" + fileId); + fileRequest.setStorage(StorageType.MINIO.name()); + return fileService.download(fileRequest); + } } diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugService.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugService.java index e2a55ee6d4..20c2a13872 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugService.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugService.java @@ -1264,11 +1264,14 @@ public class BugService { return; } String fileId = IDGenerator.nextStr(); + // 第三方平台的图片下载命名为平台名称+随机数字 + String fileName = updateBug.getPlatform() + "-" + IDGenerator.nextNum() + ".jpg"; byte[] bytes; try { // upload platform attachment to minio bytes = in.readAllBytes(); - FileCenter.getDefaultRepository().saveFile(bytes, buildBugFileRequest(updateBug.getProjectId(), updateBug.getId(), fileId, "image.png")); + // 第三方平台下载的图片默认不压缩 + FileCenter.getDefaultRepository().saveFile(bytes, buildBugFileRequest(updateBug.getProjectId(), updateBug.getId(), fileId, fileName)); } catch (Exception e) { throw new MSException(e.getMessage()); } @@ -1277,14 +1280,14 @@ public class BugService { localAttachment.setId(IDGenerator.nextStr()); localAttachment.setBugId(updateBug.getId()); localAttachment.setFileId(fileId); - localAttachment.setFileName("image.png"); + localAttachment.setFileName(fileName); localAttachment.setSize((long) bytes.length); localAttachment.setCreateTime(System.currentTimeMillis()); localAttachment.setCreateUser("admin"); localAttachment.setSource(BugAttachmentSourceType.RICH_TEXT.name()); bugLocalAttachmentMapper.insert(localAttachment); - // 替换富文本中的临时URL - updateBug.setDescription(updateBug.getDescription().replace("alt=\"" + key + "\"", "src=\"/attachment/download/file/" + updateBug.getProjectId() + "/" + fileId + "/true\"")); + // 替换富文本中的临时URL为预览URL + updateBug.setDescription(updateBug.getDescription().replace("alt=\"" + key + "\"", "src=\"/bug/attachment/preview/md/" + updateBug.getProjectId() + "/" + fileId + "/false\"")); })); } } diff --git a/backend/services/bug-management/src/test/java/io/metersphere/bug/controller/BugAttachmentControllerTests.java b/backend/services/bug-management/src/test/java/io/metersphere/bug/controller/BugAttachmentControllerTests.java index 10a02a0fd5..fc64c98adb 100644 --- a/backend/services/bug-management/src/test/java/io/metersphere/bug/controller/BugAttachmentControllerTests.java +++ b/backend/services/bug-management/src/test/java/io/metersphere/bug/controller/BugAttachmentControllerTests.java @@ -49,7 +49,7 @@ public class BugAttachmentControllerTests extends BaseTest { public static final String BUG_ATTACHMENT_CHECK_UPDATE = "/bug/attachment/check-update"; public static final String BUG_ATTACHMENT_UPDATE = "/bug/attachment/update"; public static final String BUG_ATTACHMENT_UPLOAD_MD = "/bug/attachment/upload/md/file"; - public static final String BUG_ATTACHMENT_PREVIEW_MD = "/bug/attachment/preview/md/compressed"; + public static final String BUG_ATTACHMENT_PREVIEW_MD = "/bug/attachment/preview/md"; @Test @Order(0) @@ -61,12 +61,7 @@ public class BugAttachmentControllerTests extends BaseTest { // Mock minio save file exception MockMultipartFile file = new MockMultipartFile("file", "test.txt", MediaType.APPLICATION_OCTET_STREAM_VALUE, "aa".getBytes()); this.requestUploadFile(BUG_ATTACHMENT_UPLOAD_MD, file); - BugFileSourceRequest request = new BugFileSourceRequest(); - request.setBugId("default-attachment-bug-id"); - request.setProjectId("default-project-for-attachment"); - request.setAssociated(false); - request.setFileId("not-exist-file-id"); - this.requestPostDownloadFile(BUG_ATTACHMENT_PREVIEW_MD, null, request); + this.requestGetDownloadFile(BUG_ATTACHMENT_PREVIEW_MD + "/default-project-for-attachment/not-exist-file-id/true", null); } @Test diff --git a/frontend/config/vite.config.dev.ts b/frontend/config/vite.config.dev.ts index 0ae75ad490..e7ddff02fc 100644 --- a/frontend/config/vite.config.dev.ts +++ b/frontend/config/vite.config.dev.ts @@ -33,6 +33,11 @@ export default mergeConfig( changeOrigin: true, rewrite: (path: string) => path.replace(/^\/front\/attachment/, ''), }, + '/bug/attachment': { + target: 'http://172.16.200.18:8081/', + changeOrigin: true, + rewrite: (path: string) => path.replace(/^\/front\/bug\/attachment/, ''), + }, '/plugin/image': { target: 'http://172.16.200.18:8081/', changeOrigin: true,