diff --git a/commons-asset-core/pom.xml b/commons-asset-core/pom.xml index e08416c..2076aed 100755 --- a/commons-asset-core/pom.xml +++ b/commons-asset-core/pom.xml @@ -88,6 +88,12 @@ SOFTWARE. ${thumbnailator.version} + + com.squareup + pollexor + ${pollexor.version} + + org.springframework.boot diff --git a/commons-asset-core/src/main/java/io/rocketbase/commons/config/AssetConfiguration.java b/commons-asset-core/src/main/java/io/rocketbase/commons/config/AssetConfiguration.java index 2c724bd..1cd1a86 100644 --- a/commons-asset-core/src/main/java/io/rocketbase/commons/config/AssetConfiguration.java +++ b/commons-asset-core/src/main/java/io/rocketbase/commons/config/AssetConfiguration.java @@ -2,6 +2,8 @@ import io.rocketbase.commons.service.FileStorageService; import io.rocketbase.commons.service.MongoFileStorageService; +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -9,9 +11,22 @@ import javax.annotation.Resource; +@Data @Configuration public class AssetConfiguration { + @Value("${asset.api.endpoint:/api/asset}") + private String apiEndpoint; + + @Value("${asset.api.render:/get/asset}") + private String renderEndpoint; + + @Value(value = "${asset.thumbor.host:http://localhost}") + private String thumborHost; + + @Value(value = "${asset.thumbor.key:}") + private String thumborKey; + @Resource private GridFsTemplate gridFsTemplate; diff --git a/commons-asset-core/src/main/java/io/rocketbase/commons/controller/AssetController.java b/commons-asset-core/src/main/java/io/rocketbase/commons/controller/AssetController.java index 0bb53ce..14c14cd 100644 --- a/commons-asset-core/src/main/java/io/rocketbase/commons/controller/AssetController.java +++ b/commons-asset-core/src/main/java/io/rocketbase/commons/controller/AssetController.java @@ -29,6 +29,7 @@ import javax.annotation.Resource; import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletRequest; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOutputStream; @@ -39,7 +40,7 @@ import java.util.concurrent.TimeUnit; @RestController -@RequestMapping("/api/asset") +@RequestMapping("${asset.api.endpoint:/api/asset}") @Slf4j public class AssetController implements BaseController { @@ -58,7 +59,8 @@ public class AssetController implements BaseController { @RequestMapping(method = RequestMethod.POST) public AssetRead handleFileUpload(@RequestParam("file") MultipartFile file, - @RequestParam(value = "systemRefId", required = false) String systemRefId) { + @RequestParam(value = "systemRefId", required = false) String systemRefId, + HttpServletRequest request) { if (file.isEmpty()) { throw new EmptyFileException(); } @@ -91,7 +93,7 @@ public AssetRead handleFileUpload(@RequestParam("file") MultipartFile file, AssetEntity asset = saveAndUploadAsset(assetType, tempFile, originalFilename, null, size, systemRefId); log.debug("uploaded file {} with id: {}, took: {} ms", originalFilename, asset.getId(), stopwatch.elapsed(TimeUnit.MILLISECONDS)); - return assetConverter.fromEntity(asset, getPreviewSizes(null)); + return assetConverter.fromEntity(asset, getPreviewSizes(null), request); } finally { tempFile.delete(); } @@ -102,14 +104,14 @@ public AssetRead handleFileUpload(@RequestParam("file") MultipartFile file, } @RequestMapping(method = RequestMethod.GET) - public PageableResult findAll(@RequestParam(required = false) MultiValueMap params) { + public PageableResult findAll(@RequestParam(required = false) MultiValueMap params, HttpServletRequest request) { Page pageResult = assetRepository.findAll(parsePageRequest(params)); - return PageableResult.contentPage(assetConverter.fromEntities(pageResult.getContent(), getPreviewSizes(params)), pageResult); + return PageableResult.contentPage(assetConverter.fromEntities(pageResult.getContent(), getPreviewSizes(params), request), pageResult); } @RequestMapping(value = "/{sid}", method = RequestMethod.GET) - public AssetRead getAsset(@PathVariable("sid") String sid, @RequestParam(required = false) MultiValueMap params) { - return assetConverter.fromEntity(assetRepository.getByIdOrSystemRefId(sid), getPreviewSizes(params)); + public AssetRead getAsset(@PathVariable("sid") String sid, @RequestParam(required = false) MultiValueMap params, HttpServletRequest request) { + return assetConverter.fromEntity(assetRepository.getByIdOrSystemRefId(sid), getPreviewSizes(params), request); } /** diff --git a/commons-asset-core/src/main/java/io/rocketbase/commons/controller/AssetRenderController.java b/commons-asset-core/src/main/java/io/rocketbase/commons/controller/AssetRenderController.java index e69fbb9..36150af 100644 --- a/commons-asset-core/src/main/java/io/rocketbase/commons/controller/AssetRenderController.java +++ b/commons-asset-core/src/main/java/io/rocketbase/commons/controller/AssetRenderController.java @@ -20,7 +20,7 @@ import java.util.concurrent.TimeUnit; @RestController -@RequestMapping("/get/asset") +@RequestMapping("${asset.api.render:/get/asset}") @Slf4j public class AssetRenderController implements BaseController { diff --git a/commons-asset-core/src/main/java/io/rocketbase/commons/converter/AssetConverter.java b/commons-asset-core/src/main/java/io/rocketbase/commons/converter/AssetConverter.java index 941fe41..1cc539e 100644 --- a/commons-asset-core/src/main/java/io/rocketbase/commons/converter/AssetConverter.java +++ b/commons-asset-core/src/main/java/io/rocketbase/commons/converter/AssetConverter.java @@ -1,21 +1,56 @@ package io.rocketbase.commons.converter; +import com.squareup.pollexor.Thumbor; +import io.rocketbase.commons.config.AssetConfiguration; import io.rocketbase.commons.dto.asset.AssetMeta; +import io.rocketbase.commons.dto.asset.AssetPreviews; import io.rocketbase.commons.dto.asset.AssetRead; import io.rocketbase.commons.dto.asset.PreviewSize; import io.rocketbase.commons.model.AssetEntity; +import io.rocketbase.commons.service.FileStorageService; +import io.rocketbase.commons.service.MongoFileStorageService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import javax.servlet.http.HttpServletRequest; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.stream.Collectors; @Service public class AssetConverter { - public AssetRead fromEntity(AssetEntity entity, List sizes) { + private AssetConfiguration assetConfiguration; + + private FileStorageService fileStorageService; + + private Thumbor thumbor; + + private List defaultSizes = Arrays.asList(PreviewSize.S, PreviewSize.M, PreviewSize.L); + + @Autowired + public AssetConverter(AssetConfiguration assetConfiguration, FileStorageService fileStorageService) { + this.assetConfiguration = assetConfiguration; + this.fileStorageService = fileStorageService; + } + + private boolean useLocalEndpoint() { + return fileStorageService instanceof MongoFileStorageService; + } + + public AssetRead fromEntity(AssetEntity entity, List sizes, HttpServletRequest request) { if (entity == null) { return null; } + AssetPreviews assetPreviews = AssetPreviews.builder() + .previewMap(new HashMap<>()) + .build(); + + ((sizes == null || sizes.isEmpty()) ? defaultSizes : sizes) + .forEach(s -> assetPreviews.getPreviewMap() + .put(s, getPreviewUrl(entity, s, request))); + return AssetRead.builderRead() .id(entity.getId()) .systemRefId(entity.getSystemRefId()) @@ -28,14 +63,50 @@ public AssetRead fromEntity(AssetEntity entity, List sizes) { .resolution(entity.getResolution()) .referenceUrl(entity.getReferenceUrl()) .build()) - .previews(null) + .previews(assetPreviews) .build(); } - public List fromEntities(List entities, List sizes) { + public List fromEntities(List entities, List sizes, HttpServletRequest request) { if (entities == null) { return null; } - return entities.stream().map(v -> fromEntity(v, sizes)).collect(Collectors.toList()); + return entities.stream().map(v -> fromEntity(v, sizes, request)).collect(Collectors.toList()); + } + + private String getPreviewUrl(AssetEntity entity, PreviewSize size, HttpServletRequest request) { + if (useLocalEndpoint()) { + return getBaseUrl(request) + assetConfiguration.getRenderEndpoint() + "/" + entity.getId() + "/" + size.name().toLowerCase(); + } else { + return getThumbor().buildImage(entity.getUrlPath()) + .resize(size.getMaxWidth(), size.getMaxHeight()) + .fitIn() + .toUrl(); + } + } + + private Thumbor getThumbor() { + if (thumbor == null) { + String thumborKey = assetConfiguration.getThumborKey(); + if (thumborKey.isEmpty()) { + thumbor = Thumbor.create(assetConfiguration.getThumborHost()); + } else { + thumbor = Thumbor.create(assetConfiguration.getThumborHost(), thumborKey); + } + } + return thumbor; + } + + private String getBaseUrl(HttpServletRequest request) { + String result = request.getScheme() + "://" + request.getServerName(); + int serverPort = request.getServerPort(); + if (serverPort != 80 && serverPort != 443) { + result += ":" + serverPort; + } + result += request.getContextPath(); + if (result.endsWith("/")) { + result = result.substring(0, result.length() - 1); + } + return result; } } diff --git a/commons-asset-core/src/test/java/io/rocketbase/commons/converter/AssetConverterTest.java b/commons-asset-core/src/test/java/io/rocketbase/commons/converter/AssetConverterTest.java new file mode 100644 index 0000000..6bd5111 --- /dev/null +++ b/commons-asset-core/src/test/java/io/rocketbase/commons/converter/AssetConverterTest.java @@ -0,0 +1,64 @@ +package io.rocketbase.commons.converter; + +import io.rocketbase.commons.config.AssetConfiguration; +import io.rocketbase.commons.dto.asset.AssetRead; +import io.rocketbase.commons.dto.asset.AssetType; +import io.rocketbase.commons.dto.asset.PreviewSize; +import io.rocketbase.commons.dto.asset.Resolution; +import io.rocketbase.commons.model.AssetEntity; +import io.rocketbase.commons.service.MongoFileStorageService; +import org.junit.Test; +import org.mockito.Mockito; + +import javax.servlet.http.HttpServletRequest; +import java.time.LocalDateTime; +import java.util.Arrays; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; + +public class AssetConverterTest { + + @Test + public void testFromEntityWithLocalRender() { + // given + AssetConfiguration config = new AssetConfiguration(); + config.setApiEndpoint("/api/asset"); + config.setRenderEndpoint("/get/asset"); + + HttpServletRequest mockedRequest = Mockito.mock(HttpServletRequest.class); + Mockito.when(mockedRequest.getScheme()) + .thenReturn("http"); + Mockito.when(mockedRequest.getServerName()) + .thenReturn("localhost"); + Mockito.when(mockedRequest.getServerPort()) + .thenReturn(8080); + Mockito.when(mockedRequest.getContextPath()) + .thenReturn("/"); + + String baseUrl = "http://localhost:8080" + config.getRenderEndpoint() + "/"; + + AssetConverter converter = new AssetConverter(config, new MongoFileStorageService(null)); + // when + AssetRead assetRead = converter.fromEntity(AssetEntity.builder() + .id("1235678") + .urlPath("12345678") + .fileSize(1234L) + .created(LocalDateTime.now()) + .originalFilename("originial.png") + .type(AssetType.PNG) + .systemRefId("123") + .resolution(new Resolution(100, 200)) + .build(), Arrays.asList(PreviewSize.S, PreviewSize.M, PreviewSize.L), mockedRequest); + + // then + assertThat(assetRead, notNullValue()); + assertThat(assetRead.getPreviews(), notNullValue()); + assertThat(assetRead.getPreviews().getPreviewMap().size(), equalTo(3)); + assertThat(assetRead.getPreviews().getPreviewMap().get(PreviewSize.S), equalTo(baseUrl + assetRead.getId() + "/s")); + assertThat(assetRead.getPreviews().getPreviewMap().get(PreviewSize.M), equalTo(baseUrl + assetRead.getId() + "/m")); + assertThat(assetRead.getPreviews().getPreviewMap().get(PreviewSize.L), equalTo(baseUrl + assetRead.getId() + "/l")); + + } +} \ No newline at end of file