diff --git a/web-services-app/src/main/java/edu/unc/lib/boxc/web/services/processing/DownloadImageService.java b/web-services-app/src/main/java/edu/unc/lib/boxc/web/services/processing/DownloadImageService.java index 7a00756abb..78170ca690 100644 --- a/web-services-app/src/main/java/edu/unc/lib/boxc/web/services/processing/DownloadImageService.java +++ b/web-services-app/src/main/java/edu/unc/lib/boxc/web/services/processing/DownloadImageService.java @@ -6,6 +6,7 @@ import edu.unc.lib.boxc.search.api.models.Datastream; import edu.unc.lib.boxc.web.services.utils.ImageServerUtil; import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -68,11 +69,7 @@ public String getSize(ContentObjectRecord contentObjectRecord, String size) { if (integerSize <= 0 ) { throw new IllegalArgumentException(INVALID_SIZE_MESSAGE); } else { - // format of dimensions is like 800x1200 - var datastreamObject = getDatastream(contentObjectRecord); - String dimensions = datastreamObject.getExtent(); - String[] dimensionParts = dimensions.split("x"); - int longerSide = Math.max(Integer.parseInt(dimensionParts[0]), Integer.parseInt(dimensionParts[1])); + var longerSide = getLongestSide(contentObjectRecord); // request is bigger than or equal to full size, so we will switch to full size if (integerSize >= longerSide) { return FULL_SIZE; @@ -106,6 +103,30 @@ private Datastream getDatastream(ContentObjectRecord contentObjectRecord) { return contentObjectRecord.getDatastreamObject(id); } + /** + * Get the extent value for the JP2 datastream, falling back to the original file if not set + * @param contentObjectRecord + * @return extent in string format + */ + private String getExtent(ContentObjectRecord contentObjectRecord) { + var ds = contentObjectRecord.getDatastreamObject(DatastreamType.JP2_ACCESS_COPY.getId()); + String extent = ds == null ? null : ds.getExtent(); + if (StringUtils.isEmpty(extent)) { + ds = contentObjectRecord.getDatastreamObject(DatastreamType.ORIGINAL_FILE.getId()); + } + return ds.getExtent(); + } + + private int getLongestSide(ContentObjectRecord contentObjectRecord) { + var extent = getExtent(contentObjectRecord); + if (StringUtils.isEmpty(extent)) { + return 0; + } + // format of dimensions is like 800x1200, heightxwidth + String[] dimensionParts = extent.split("x"); + return Math.max(Integer.parseInt(dimensionParts[0]), Integer.parseInt(dimensionParts[1])); + } + public void setIiifBasePath(String iiifBasePath) { this.iiifBasePath = iiifBasePath; } diff --git a/web-services-app/src/test/java/edu/unc/lib/boxc/web/services/rest/DownloadImageControllerIT.java b/web-services-app/src/test/java/edu/unc/lib/boxc/web/services/rest/DownloadImageControllerIT.java index 2d54041815..b32c15994b 100644 --- a/web-services-app/src/test/java/edu/unc/lib/boxc/web/services/rest/DownloadImageControllerIT.java +++ b/web-services-app/src/test/java/edu/unc/lib/boxc/web/services/rest/DownloadImageControllerIT.java @@ -113,6 +113,7 @@ public void testGetImageAtPixelSizeSmallerThanFull() throws Exception { when(originalDatastream.getExtent()).thenReturn("1200x1200"); when(originalDatastream.getFilename()).thenReturn(filename); when(contentObjectSolrRecord.getDatastreamObject(DatastreamType.JP2_ACCESS_COPY.getId())).thenReturn(jp2Datastream); + when(jp2Datastream.getExtent()).thenReturn("1200x1200"); when(contentObjectSolrRecord.getPid()).thenReturn(pid); MvcResult result = mvc.perform(get("/downloadImage/" + pidString + "/800")) @@ -197,6 +198,40 @@ public void testGetImageAtPixelSizeBiggerThanFullNoPermission() throws Exception assertEquals("Insufficient permissions", response.getContentAsString()); } + @Test + public void testGetImageFromJp2AtPixelSizeBiggerThanFull() throws Exception { + var pid = makePid(); + var pidString = pid.getId(); + var formattedPid = ImageServerUtil.getImageServerEncodedId(pidString); + var filename = "bunny.jpg"; + ContentObjectSolrRecord contentObjectSolrRecord = mock(ContentObjectSolrRecord.class); + Datastream originalDatastream = mock(Datastream.class); + Datastream jp2Datastream = mock(Datastream.class); + + stubFor(WireMock.get(urlMatching("/" + formattedPid + "/full/max/0/default.jpg")) + .willReturn(aResponse() + .withStatus(HttpStatus.OK.value()) + .withBodyFile(filename) + .withHeader("Content-Type", "image/jpeg"))); + + when(solrSearchService.getObjectById(any(SimpleIdRequest.class))).thenReturn(contentObjectSolrRecord); + when(contentObjectSolrRecord.getDatastreamObject("original_file")).thenReturn(originalDatastream); + when(originalDatastream.getExtent()).thenReturn("9999x3333"); + when(originalDatastream.getFilename()).thenReturn(filename); + when(contentObjectSolrRecord.getDatastreamObject(DatastreamType.JP2_ACCESS_COPY.getId())).thenReturn(jp2Datastream); + when(jp2Datastream.getExtent()).thenReturn("1200x1300"); + when(contentObjectSolrRecord.getPid()).thenReturn(pid); + + MvcResult result = mvc.perform(get("/downloadImage/" + pidString + "/1800")) + .andExpect(status().is2xxSuccessful()) + .andReturn(); + + var response = result.getResponse(); + + assertEquals("attachment; filename=bunny_max.jpg", response.getHeader(CONTENT_DISPOSITION)); + assertCorrectImageReturned(response); + } + @Test public void testGetImageNoViewReducedPermission() throws Exception { var pid = makePid(); @@ -282,6 +317,32 @@ public void testGetImageNoJP2() throws Exception { .andReturn(); } + @Test + public void testGetImageFailToStream() throws Exception { + var pid = makePid(); + var pidString = pid.getId(); + var formattedPid = ImageServerUtil.getImageServerEncodedId(pidString); + var filename = "bunny.jpg"; + ContentObjectSolrRecord contentObjectSolrRecord = mock(ContentObjectSolrRecord.class); + Datastream originalDatastream = mock(Datastream.class); + Datastream jp2Datastream = mock(Datastream.class); + + when(solrSearchService.getObjectById(any(SimpleIdRequest.class))).thenReturn(contentObjectSolrRecord); + when(contentObjectSolrRecord.getDatastreamObject(DatastreamType.ORIGINAL_FILE.getId())).thenReturn(originalDatastream); + when(originalDatastream.getFilename()).thenReturn(filename); + when(contentObjectSolrRecord.getDatastreamObject(DatastreamType.JP2_ACCESS_COPY.getId())).thenReturn(jp2Datastream); + + when(contentObjectSolrRecord.getPid()).thenReturn(pid); + + stubFor(WireMock.get(urlMatching("/" + formattedPid + "/full/max/0/default.jpg")) + .willReturn(aResponse() + .withStatus(HttpStatus.BAD_REQUEST.value()))); + + MvcResult result = mvc.perform(get("/downloadImage/" + pidString + "/max")) + .andExpect(status().isNotFound()) + .andReturn(); + } + private void assertCorrectImageReturned(MockHttpServletResponse response) throws IOException { assertEquals("image/jpeg", response.getContentType());