Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bxc 4355 update download image #1637

Merged
merged 4 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading