
de.digitalcollections.iiif.hymir.image.frontend.IIIFImageApiController Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of iiif-server-hymir Show documentation
Show all versions of iiif-server-hymir Show documentation
Hymir is a Java based IIIF Server. It is based on "IIIF API Java Libraries" (Java implementations of the IIIF specifications, see https://github.com/dbmdz/iiif-apis).
package de.digitalcollections.iiif.hymir.image.frontend;
import de.digitalcollections.commons.springboot.metrics.MetricsService;
import de.digitalcollections.iiif.hymir.config.CustomResponseHeaders;
import de.digitalcollections.iiif.hymir.image.business.api.ImageService;
import de.digitalcollections.iiif.hymir.model.exception.InvalidParametersException;
import de.digitalcollections.iiif.hymir.model.exception.UnsupportedFormatException;
import de.digitalcollections.iiif.model.image.ImageApiProfile;
import de.digitalcollections.iiif.model.image.ImageApiSelector;
import de.digitalcollections.iiif.model.image.ResolvingException;
import de.digitalcollections.iiif.model.jackson.IiifObjectMapper;
import de.digitalcollections.model.api.identifiable.resource.exceptions.ResourceNotFoundException;
import java.awt.Dimension;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.WebRequest;
@Controller
@RequestMapping("${custom.iiif.image.urlPrefix:/image/v2/}")
public class IIIFImageApiController {
public static final String VERSION = "v2";
@Value("${custom.iiif.image.canonicalRedirect:true}")
private boolean isCanonicalRedirectEnabled;
@Autowired
protected CustomResponseHeaders customResponseHeaders;
private final ImageService imageService;
private final IiifObjectMapper objectMapper;
@Autowired
private MetricsService metricsService;
@Autowired
public IIIFImageApiController(ImageService imageService, IiifObjectMapper objectMapper) {
this.imageService = imageService;
this.objectMapper = objectMapper;
}
void setCanonicalRedirectEnabled(boolean canonicalRedirectEnabled) {
isCanonicalRedirectEnabled = canonicalRedirectEnabled;
}
/**
* Get the base URL for all Image API URLs from the request.
*
* This will handle cases such as reverse-proxying and SSL-termination on the frontend server
*/
private String getUrlBase(HttpServletRequest request) {
String scheme = request.getHeader("X-Forwarded-Proto");
if (scheme == null) {
scheme = request.getScheme();
}
String host = request.getHeader("X-Forwarded-Host");
if (host == null) {
host = request.getHeader("Host");
}
if (host == null) {
host = request.getRemoteHost();
}
String base = String.format("%s://%s", scheme, host);
if (!request.getContextPath().isEmpty()) {
base += request.getContextPath();
}
return base;
}
@RequestMapping(value = "{identifier}/{region}/{size}/{rotation}/{quality}.{format}")
public ResponseEntity getImageRepresentation(
@PathVariable String identifier, @PathVariable String region,
@PathVariable String size, @PathVariable String rotation,
@PathVariable String quality, @PathVariable String format,
HttpServletRequest request, HttpServletResponse response, WebRequest webRequest)
throws UnsupportedFormatException, UnsupportedOperationException, IOException, InvalidParametersException, ResourceNotFoundException {
HttpHeaders headers = new HttpHeaders();
String path;
if (request.getPathInfo() != null) {
path = request.getPathInfo();
} else {
path = request.getServletPath();
}
long modified = imageService.getImageModificationDate(identifier).toEpochMilli();
webRequest.checkNotModified(modified);
headers.setDate("Last-Modified", modified);
ImageApiSelector selector = new ImageApiSelector();
try {
selector.setIdentifier(identifier);
selector.setRegion(region);
selector.setSize(size);
selector.setRotation(rotation);
if (quality.equals("native")) {
quality = "default";
}
selector.setQuality(ImageApiProfile.Quality.valueOf(quality.toUpperCase()));
selector.setFormat(ImageApiProfile.Format.valueOf(format.toUpperCase()));
} catch (ResolvingException e) {
throw new InvalidParametersException(e);
}
de.digitalcollections.iiif.model.image.ImageService info = new de.digitalcollections.iiif.model.image.ImageService(
"http://foo.org/" + identifier);
imageService.readImageInfo(identifier, info);
ImageApiProfile profile = ImageApiProfile.merge(info.getProfiles());
String canonicalForm;
try {
canonicalForm = selector.getCanonicalForm(
new Dimension(info.getWidth(), info.getHeight()),
profile, ImageApiProfile.Quality.COLOR); // TODO: Make this variable on the actual image
} catch (ResolvingException e) {
throw new InvalidParametersException(e);
}
String canonicalUrl = getUrlBase(request) + path.substring(0, path.indexOf(identifier)) + canonicalForm;
if (this.isCanonicalRedirectEnabled && !canonicalForm.equals(selector.toString())) {
response.setHeader("Link", String.format("<%s>;rel=\"canonical\"", canonicalUrl));
response.sendRedirect(canonicalUrl);
return null;
} else {
headers.add("Link", String.format("<%s>;rel=\"canonical\"", canonicalUrl));
final String mimeType = selector.getFormat().getMimeType().getTypeName();
headers.setContentType(MediaType.parseMediaType(mimeType));
String filename = path.replaceFirst("/image/", "").replace('/', '_').replace(',', '_');
headers.set("Content-Disposition", "inline; filename=" + filename);
headers.add("Link", String.format("<%s>;rel=\"profile\"", info.getProfiles().get(0).getIdentifier().toString()));
ByteArrayOutputStream os = new ByteArrayOutputStream();
long duration = System.currentTimeMillis();
imageService.processImage(identifier, selector, profile, os);
duration = System.currentTimeMillis() - duration;
metricsService.increaseCounterWithDurationAndPercentiles("image", "process", duration);
customResponseHeaders.forImageTile().forEach(customResponseHeader -> {
headers.set(customResponseHeader.getName(), customResponseHeader.getValue());
});
return new ResponseEntity<>(os.toByteArray(), headers, HttpStatus.OK);
}
}
@RequestMapping(value = "{identifier}/info.json", method = {RequestMethod.GET, RequestMethod.HEAD})
public ResponseEntity getInfo(@PathVariable String identifier, HttpServletRequest req,
WebRequest webRequest) throws Exception {
long duration = System.currentTimeMillis();
long modified = imageService.getImageModificationDate(identifier).toEpochMilli();
webRequest.checkNotModified(modified);
String path;
if (req.getPathInfo() != null) {
path = req.getPathInfo();
} else {
path = req.getServletPath();
}
String baseUrl = getUrlBase(req);
de.digitalcollections.iiif.model.image.ImageService info = new de.digitalcollections.iiif.model.image.ImageService(
baseUrl + path.replace("/info.json", "").replace(identifier, URLEncoder.encode(identifier, "UTF-8")));
imageService.readImageInfo(identifier, info);
duration = System.currentTimeMillis() - duration;
metricsService.increaseCounterWithDurationAndPercentiles("generations", "infojson", duration);
HttpHeaders headers = new HttpHeaders();
headers.setDate("Last-Modified", modified);
String contentType = req.getHeader("Accept");
if (contentType != null && contentType.equals("application/ld+json")) {
headers.set("Content-Type", contentType);
} else {
headers.set("Content-Type", "application/json");
headers.add("Link", "; "
+ "rel=\"http://www.w3.org/ns/json-ld#context\"; "
+ "type=\"application/ld+json\"");
}
headers.add("Link", String.format("<%s>;rel=\"profile\"", info.getProfiles().get(0).getIdentifier().toString()));
headers.add("Access-Control-Allow-Origin", "*");
customResponseHeaders.forImageInfo().forEach(customResponseHeader -> {
headers.set(customResponseHeader.getName(), customResponseHeader.getValue());
});
return new ResponseEntity<>(objectMapper.writeValueAsString(info), headers, HttpStatus.OK);
}
@RequestMapping(value = "{identifier}", method = {RequestMethod.GET, RequestMethod.HEAD})
public String getInfoRedirect(@PathVariable String identifier, HttpServletResponse response) {
response.setHeader("Access-Control-Allow-Origin", "*");
return "redirect:/image/" + VERSION + "/" + identifier + "/info.json";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy