Skip to content

Commit

Permalink
Bxc 4355 update download image (#1637)
Browse files Browse the repository at this point in the history
* BXC-4355 update full to max for iiifv3

* BXC-4355 clean up code and add util class

* BXC-4355 replace bean

---------

Co-authored-by: Sharon Luong <[email protected]>
  • Loading branch information
sharonluong and Sharon Luong authored Dec 7, 2023
1 parent 8be5fb0 commit bdd46e2
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 33 deletions.
2 changes: 1 addition & 1 deletion static/js/vue-cdr-access/src/mixins/fileDownloadUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export default {
}


html += `<a href="${this.imgDownloadLink(brief_object.id, 'full')}" class="dropdown-item">${this.$t('full_record.full_size')} JPG</a>`;
html += `<a href="${this.imgDownloadLink(brief_object.id, 'max')}" class="dropdown-item">${this.$t('full_record.full_size')} JPG</a>`;
html += '<hr class="dropdown-divider">';
html += `<a href="/indexablecontent/${brief_object.id}?dl=true" class="dropdown-item">${this.$t('full_record.original_file')}</a>`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import edu.unc.lib.boxc.model.api.DatastreamType;
import edu.unc.lib.boxc.search.api.models.ContentObjectRecord;
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.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
Expand All @@ -22,13 +23,13 @@
*/
public class DownloadImageService {
private String iiifBasePath;
public static final String FULL_SIZE = "full";
public static final String FULL_SIZE = "max";
public static final String INVALID_SIZE_MESSAGE = "Unable to determine size for access copy download";

/**
* Method contacts the IIIF server for the requested access copy image and returns it
* @param contentObjectRecord solr record of the file
* @param size a string which is either "full" for full size or a pixel length like "1200"
* @param size a string which is either "max" for full size or a pixel length like "1200"
* @param pidString the UUID of the file
* @return a response entity which contains headers and content of the access copy image
* @throws IOException
Expand All @@ -54,13 +55,12 @@ public ResponseEntity<InputStreamResource> streamImage(ContentObjectRecord conte
/**
* A method that builds the IIIF URL based on an assumption of full region, 0 rotation, and default quality.
* @param id the UUID of the file
* @param size a string which is either "full" for full size or a pixel length like "1200"
* @param size a string which is either "max" for full size or a pixel length like "1200"
* @return a string which is the URL to request the IIIF server for the image
*/
private String buildURL(String id, String size) {
var formattedSize = size;
var hash = idToPath(id, 4, 2);
var formattedId = hash + id + ".jp2";
var formattedId = ImageServerUtil.getImageServerEncodedId(id);
if (!Objects.equals(size, FULL_SIZE)) {
// pixel length should be in !123,123 format
formattedSize = "!" + size + "," + size;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import edu.unc.lib.boxc.common.util.URIUtil;
import edu.unc.lib.boxc.web.common.exceptions.ClientAbortException;
import edu.unc.lib.boxc.web.common.utils.FileIOUtil;
import edu.unc.lib.boxc.web.services.utils.ImageServerUtil;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
Expand Down Expand Up @@ -59,7 +60,7 @@ public void getMetadata(String id, OutputStream outStream,
HttpServletResponse response, int retryServerError) {

var path = new StringBuilder(getImageServerProxyBasePath());
path.append(getImageServerEncodedId(id)).append(".jp2").append("/info.json");
path.append(ImageServerUtil.getImageServerEncodedId(id)).append("/info.json");

int statusCode = -1;
String statusLine = null;
Expand Down Expand Up @@ -115,7 +116,7 @@ public void streamJP2(String id, String region, String size, String rotation, St
int retryServerError) {

StringBuilder path = new StringBuilder(getImageServerProxyBasePath());
path.append(getImageServerEncodedId(id)).append(".jp2")
path.append(ImageServerUtil.getImageServerEncodedId(id))
.append("/" + region).append("/" + size)
.append("/" + rotation).append("/" + quality + "." + format);

Expand Down Expand Up @@ -149,17 +150,6 @@ public void streamJP2(String id, String region, String size, String rotation, St
}
}

/**
* Returns the image server base path with encoded IDs
* @param id
* @return
*/
public String getImageServerEncodedId(String id) {
var idPathEncoded = URLEncoder.encode(idToPath(id, 4, 2), StandardCharsets.UTF_8);
var idEncoded = URLEncoder.encode(id, StandardCharsets.UTF_8);
return idPathEncoded + idEncoded;
}

public void setImageServerProxyBasePath(String fullPath) {
this.imageServerProxyBasePath = fullPath;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package edu.unc.lib.boxc.web.services.utils;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

import static edu.unc.lib.boxc.model.fcrepo.ids.RepositoryPaths.idToPath;

/**
* @author snluong
*/
public class ImageServerUtil {
/**
* Returns the object ID in proper encoded format with .jp2 extension
* @param id
* @return
*/
public static String getImageServerEncodedId(String id) {
var idPathEncoded = URLEncoder.encode(idToPath(id, 4, 2), StandardCharsets.UTF_8);
var idEncoded = URLEncoder.encode(id, StandardCharsets.UTF_8);
return idPathEncoded + idEncoded + ".jp2";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
import edu.unc.lib.boxc.search.solr.models.ContentObjectSolrRecord;
import edu.unc.lib.boxc.web.common.services.SolrQueryLayerService;
import edu.unc.lib.boxc.web.services.processing.DownloadImageService;
import edu.unc.lib.boxc.web.services.processing.ImageServerProxyService;
import edu.unc.lib.boxc.web.services.rest.modify.AbstractAPIIT;
import edu.unc.lib.boxc.web.services.utils.ImageServerUtil;
import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand Down Expand Up @@ -64,7 +66,7 @@ public class DownloadImageControllerIT extends AbstractAPIIT {
public void testGetImageAtFullSize() throws Exception {
var pid = makePid();
var pidString = pid.getId();
var formattedPid = idToPath(pidString, 4, 2) + pidString + ".jp2";
var formattedPid = ImageServerUtil.getImageServerEncodedId(pidString);
var filename = "bunny.jpg";
ContentObjectSolrRecord contentObjectSolrRecord = mock(ContentObjectSolrRecord.class);
Datastream originalDatastream = mock(Datastream.class);
Expand All @@ -77,25 +79,25 @@ public void testGetImageAtFullSize() throws Exception {

when(contentObjectSolrRecord.getPid()).thenReturn(pid);

stubFor(WireMock.get(urlMatching("/" + formattedPid + "/full/full/0/default.jpg"))
stubFor(WireMock.get(urlMatching("/" + formattedPid + "/full/max/0/default.jpg"))
.willReturn(aResponse()
.withStatus(HttpStatus.OK.value())
.withBodyFile(filename)
.withHeader("Content-Type", "image/jpeg")));

MvcResult result = mvc.perform(get("/downloadImage/" + pidString + "/full"))
MvcResult result = mvc.perform(get("/downloadImage/" + pidString + "/max"))
.andExpect(status().is2xxSuccessful())
.andReturn();

var response = result.getResponse();
assertEquals("attachment; filename=bunny_full.jpg", response.getHeader(CONTENT_DISPOSITION));
assertEquals("attachment; filename=bunny_max.jpg", response.getHeader(CONTENT_DISPOSITION));
}

@Test
public void testGetImageAtPixelSizeSmallerThanFull() throws Exception {
var pid = makePid();
var pidString = pid.getId();
var formattedPid = idToPath(pidString, 4, 2) + pidString + ".jp2";
var formattedPid = ImageServerUtil.getImageServerEncodedId(pidString);
var filename = "bunny.jpg";
ContentObjectSolrRecord contentObjectSolrRecord = mock(ContentObjectSolrRecord.class);
Datastream originalDatastream = mock(Datastream.class);
Expand Down Expand Up @@ -128,13 +130,13 @@ public void testGetImageAtPixelSizeSmallerThanFull() throws Exception {
public void testGetImageAtPixelSizeBiggerThanFull() throws Exception {
var pid = makePid();
var pidString = pid.getId();
var formattedPid = idToPath(pidString, 4, 2) + pidString + ".jp2";
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/full/0/default.jpg"))
stubFor(WireMock.get(urlMatching("/" + formattedPid + "/full/max/0/default.jpg"))
.willReturn(aResponse()
.withStatus(HttpStatus.OK.value())
.withBodyFile(filename)
Expand All @@ -153,7 +155,7 @@ public void testGetImageAtPixelSizeBiggerThanFull() throws Exception {

var response = result.getResponse();

assertEquals("attachment; filename=bunny_full.jpg", response.getHeader(CONTENT_DISPOSITION));
assertEquals("attachment; filename=bunny_max.jpg", response.getHeader(CONTENT_DISPOSITION));
assertCorrectImageReturned(response);
}

Expand All @@ -165,7 +167,7 @@ public void testFullSizeAccessImageNoFullSizePermissions() throws Exception {
doThrow(new AccessRestrictionException()).when(accessControlService)
.assertHasAccess(anyString(), eq(filePid), any(AccessGroupSetImpl.class), eq(viewOriginal));

MvcResult result = mvc.perform(get("/downloadImage/" + filePid.getId() + "/full"))
MvcResult result = mvc.perform(get("/downloadImage/" + filePid.getId() + "/max"))
.andExpect(status().isForbidden())
.andReturn();

Expand Down Expand Up @@ -276,7 +278,7 @@ public void testGetImageNoJP2() throws Exception {
ContentObjectSolrRecord contentObjectSolrRecord = mock(ContentObjectSolrRecord.class);
when(solrSearchService.getObjectById(any(SimpleIdRequest.class))).thenReturn(contentObjectSolrRecord);

mvc.perform(get("/downloadImage/" + filePid.getId() + "/full"))
mvc.perform(get("/downloadImage/" + filePid.getId() + "/max"))
.andExpect(status().is4xxClientError())
.andReturn();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import edu.unc.lib.boxc.auth.fcrepo.models.AccessGroupSetImpl;
import edu.unc.lib.boxc.web.services.processing.ImageServerProxyService;
import edu.unc.lib.boxc.web.services.rest.modify.AbstractAPIIT;
import edu.unc.lib.boxc.web.services.utils.ImageServerUtil;
import org.apache.http.impl.client.CloseableHttpClient;
import org.junit.Assert;
import org.junit.jupiter.api.AfterEach;
Expand Down Expand Up @@ -44,8 +45,6 @@

@WireMockTest(httpPort = 46887)
public class ImageServerProxyControllerTest extends AbstractAPIIT {
@Autowired
private ImageServerProxyService imageServerProxyService;
@Autowired
private AccessControlService accessControlService;

Expand Down Expand Up @@ -78,7 +77,7 @@ void getRegionTestNoAccess() throws Exception {
void getRegionTest() throws Exception {
var pid = makePid();
var pidString = pid.getId();
var formattedBasePath = "/iiif/v3/" + imageServerProxyService.getImageServerEncodedId(pidString) + ".jp2";
var formattedBasePath = "/iiif/v3/" + ImageServerUtil.getImageServerEncodedId(pidString);
var filename = "bunny.jpg";
when(accessControlService.hasAccess(any(), any(), any())).thenReturn(true);
stubFor(WireMock.get(urlMatching(formattedBasePath + "/full/max/0/default.jpg"))
Expand Down Expand Up @@ -111,7 +110,7 @@ void getMetadataTestNoAccess() throws Exception {
void getMetadataTest() throws Exception {
var pid = makePid();
var pidString = pid.getId();
var formattedBasePath = "/iiif/v3/" + imageServerProxyService.getImageServerEncodedId(pidString) + ".jp2";
var formattedBasePath = "/iiif/v3/" + ImageServerUtil.getImageServerEncodedId(pidString);
var json = "{\"@context\":\"http://iiif.io/api/image/3/context.json\",\"id\":\"http://example.com/iiif/v3/"
+ pidString + "\",\"type\":\"ImageService3\",\"protocol\":\"http://iiif.io/api/image\"}";
when(accessControlService.hasAccess(any(), any(), any())).thenReturn(true);
Expand Down

0 comments on commit bdd46e2

Please sign in to comment.