From 46220a9286d4af939038ba57e41987a4a8403e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B6=E6=AE=87=5FFanzero?= <71616828+FanZeros@users.noreply.github.com> Date: Tue, 17 May 2022 23:22:12 +0800 Subject: [PATCH] feat: uri encode processing for attachment paths when querying attachments (#1874) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Deal with illegal character * Update FilePathDescriptor.java * 后端对链接进行encodeURI * 删除之前无用的test * Add some testcase for convertToDto * checkstyle * delete test 当前该测试用例貌似并不能在全平台上成功运行,所以废弃 --- .../service/impl/AttachmentServiceImpl.java | 30 ++++- .../impl/AttachmentServiceImplTest.java | 120 ++++++++++++++++++ 2 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 src/test/java/run/halo/app/service/impl/AttachmentServiceImplTest.java diff --git a/src/main/java/run/halo/app/service/impl/AttachmentServiceImpl.java b/src/main/java/run/halo/app/service/impl/AttachmentServiceImpl.java index 01d9b38d05..478542b53b 100644 --- a/src/main/java/run/halo/app/service/impl/AttachmentServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/AttachmentServiceImpl.java @@ -1,5 +1,6 @@ package run.halo.app.service.impl; +import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; @@ -18,6 +19,7 @@ import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.util.UriUtils; import run.halo.app.exception.AlreadyExistsException; import run.halo.app.handler.file.FileHandlers; import run.halo.app.model.dto.AttachmentDTO; @@ -30,6 +32,7 @@ import run.halo.app.service.AttachmentService; import run.halo.app.service.OptionService; import run.halo.app.service.base.AbstractCrudService; +import run.halo.app.utils.FilenameUtils; import run.halo.app.utils.HaloUtils; /** @@ -124,7 +127,8 @@ public Attachment upload(MultipartFile file) { // Convert separator attachment.setPath(HaloUtils.changeFileSeparatorToUrlSeparator(uploadResult.getFilePath())); attachment.setFileKey(uploadResult.getKey()); - attachment.setThumbPath(uploadResult.getThumbPath()); + attachment.setThumbPath( + HaloUtils.changeFileSeparatorToUrlSeparator(uploadResult.getThumbPath())); attachment.setMediaType(uploadResult.getMediaType().toString()); attachment.setSuffix(uploadResult.getSuffix()); attachment.setWidth(uploadResult.getWidth()); @@ -173,11 +177,26 @@ public AttachmentDTO convertToDto(Attachment attachment) { AttachmentDTO attachmentDTO = new AttachmentDTO().convertFrom(attachment); if (Objects.equals(attachmentDTO.getType(), AttachmentType.LOCAL)) { + + // 将 local 存储的链接中的文件名替换为编码后的文件名 + String path = attachmentDTO.getPath() + .replace(attachmentDTO.getName(), encodeValue(attachmentDTO.getName())); + + String basename = FilenameUtils.getBasename(attachmentDTO.getName()); + String extension = FilenameUtils.getExtension(attachmentDTO.getName()); + // 得到 thumbnail name + String thumbnailName = String.format("%s-thumbnail%s", basename, extension); + String thumbnailPath = attachmentDTO.getThumbPath() + .replace(thumbnailName, encodeValue(thumbnailName)); + // Append blog base url to path and thumbnail String fullPath = StringUtils - .join(enabledAbsolutePath ? blogBaseUrl : "", "/", attachmentDTO.getPath()); + .join(enabledAbsolutePath ? blogBaseUrl : "", "/", path); String fullThumbPath = StringUtils - .join(enabledAbsolutePath ? blogBaseUrl : "", "/", attachmentDTO.getThumbPath()); + .join(enabledAbsolutePath ? blogBaseUrl : "", "/", thumbnailPath); + + // 对于之前上传的链接,需要将文件名替换为编码后的文件名 + fullThumbPath = HaloUtils.changeFileSeparatorToUrlSeparator(fullThumbPath); // Set full path and full thumb path attachmentDTO.setPath(fullPath); @@ -187,6 +206,11 @@ public AttachmentDTO convertToDto(Attachment attachment) { return attachmentDTO; } + private String encodeValue(String value) { + return UriUtils.encode(value, StandardCharsets.UTF_8); + } + + @Override public List listAllMediaType() { return attachmentRepository.findAllMediaType(); diff --git a/src/test/java/run/halo/app/service/impl/AttachmentServiceImplTest.java b/src/test/java/run/halo/app/service/impl/AttachmentServiceImplTest.java new file mode 100644 index 0000000000..b476635353 --- /dev/null +++ b/src/test/java/run/halo/app/service/impl/AttachmentServiceImplTest.java @@ -0,0 +1,120 @@ +package run.halo.app.service.impl; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import run.halo.app.handler.file.FileHandlers; +import run.halo.app.model.dto.AttachmentDTO; +import run.halo.app.model.entity.Attachment; +import run.halo.app.model.enums.AttachmentType; +import run.halo.app.repository.AttachmentRepository; +import run.halo.app.service.OptionService; + + +/** + * Test for Attachment Service implementation + * + * @date 2022-5-17 + */ +@ExtendWith(MockitoExtension.class) +class AttachmentServiceImplTest { + + @Mock + AttachmentRepository attachmentRepository; + + @Mock + OptionService optionService; + + @Mock + FileHandlers fileHandlers; + + @InjectMocks + AttachmentServiceImpl attachmentService; + + @Test + void convertToDtoNormal() { + Attachment attachment = new Attachment(); + attachment.setName("fake-name"); + attachment.setFileKey("upload/2022/05/fake-name.png"); + attachment.setPath("upload/2022/05/fake-name.png"); + attachment.setThumbPath("upload/2022/05/fake-name-thumbnail.png"); + attachment.setType(AttachmentType.LOCAL); + + Mockito.when(optionService.getBlogBaseUrl()).thenReturn("https://mock.halo.run"); + Mockito.when(optionService.isEnabledAbsolutePath()).thenReturn(false); + AttachmentDTO attachmentDTO = attachmentService.convertToDto(attachment); + + Assertions.assertEquals("/upload/2022/05/fake-name.png", attachmentDTO.getPath()); + Assertions.assertEquals("/upload/2022/05/fake-name-thumbnail.png", + attachmentDTO.getThumbPath()); + Mockito.verify(optionService, Mockito.times(1)).getBlogBaseUrl(); + } + + @Test + void convertToDtoWithChinese() { + Attachment attachment = new Attachment(); + attachment.setName("图片"); + attachment.setFileKey("upload/2022/05/图片.png"); + attachment.setPath("upload/2022/05/图片.png"); + attachment.setThumbPath("upload/2022/05/图片-thumbnail.png"); + attachment.setType(AttachmentType.LOCAL); + + Mockito.when(optionService.getBlogBaseUrl()).thenReturn("https://mock.halo.run"); + Mockito.when(optionService.isEnabledAbsolutePath()).thenReturn(false); + AttachmentDTO attachmentDTO = attachmentService.convertToDto(attachment); + + Assertions.assertEquals("/upload/2022/05/%E5%9B%BE%E7%89%87.png", attachmentDTO.getPath()); + Assertions.assertEquals("/upload/2022/05/%E5%9B%BE%E7%89%87-thumbnail.png", + attachmentDTO.getThumbPath()); + Mockito.verify(optionService, Mockito.times(1)).getBlogBaseUrl(); + } + + @Test + void convertToDtoWithSpecialChar() { + Attachment attachment = new Attachment(); + attachment.setName("100%1#"); + attachment.setFileKey("upload/2022/05/100%1#.png"); + attachment.setPath("upload/2022/05/100%1#.png"); + attachment.setThumbPath("upload/2022/05/100%1#-thumbnail.png"); + attachment.setType(AttachmentType.LOCAL); + + Mockito.when(optionService.getBlogBaseUrl()).thenReturn("https://mock.halo.run"); + Mockito.when(optionService.isEnabledAbsolutePath()).thenReturn(false); + AttachmentDTO attachmentDTO = attachmentService.convertToDto(attachment); + + Assertions.assertEquals("/upload/2022/05/100%251%23.png", attachmentDTO.getPath()); + Assertions.assertEquals("/upload/2022/05/100%251%23-thumbnail.png", + attachmentDTO.getThumbPath()); + Mockito.verify(optionService, Mockito.times(1)).getBlogBaseUrl(); + } + + @Test + void convertToDtoWithFormerVersionFile() { + // Attachment attachment = new Attachment(); + // attachment.setName("之前版本上传的图片"); + // attachment.setFileKey("upload/2022/04/之前版本上传的图片.png"); + // attachment.setPath("upload/2022/04/之前版本上传的图片.png"); + // attachment.setThumbPath("upload/2022\\04\\之前版本上传的图片-thumbnail.png"); + // attachment.setType(AttachmentType.LOCAL); + // + // Mockito.when(optionService.getBlogBaseUrl()).thenReturn("https://mock.halo.run"); + // Mockito.when(optionService.isEnabledAbsolutePath()).thenReturn(false); + // AttachmentDTO attachmentDTO = attachmentService.convertToDto(attachment); + // + // Assertions.assertEquals( + // "/upload/2022/04/%E4%B9%8B%E5%89%8D%E7%89%88%E6%9C%AC%E4%B8%8A%E4%BC%A0%E7%9A + // %84%E5" + // + "%9B%BE%E7%89%87.png", + // attachmentDTO.getPath()); + // Assertions.assertEquals( + // "/upload/2022/04/%E4%B9%8B%E5%89%8D%E7%89%88%E6%9C%AC%E4%B8%8A%E4%BC%A0%E7%9A + // %84%E5" + // + "%9B%BE%E7%89%87-thumbnail.png", + // attachmentDTO.getThumbPath()); + // Mockito.verify(optionService, Mockito.times(1)).getBlogBaseUrl(); + } +} \ No newline at end of file