Overview

This is a reference guide on how to launch Cantaloupe, a IIIF image server, on EC2.

https://cantaloupe-project.github.io/

Additionally, this article introduces an example of Delegate Methods for restricting image download sizes. Specifically, it addresses cases where an error occurs when attempting to retrieve a full-size image via /full/full/.

https://cantaloupe-project.github.io/manual/5.0/access-control.html

Setting Up Cantaloupe

Creating an EC2 Instance

I created an EC2 instance with the platform set to Ubuntu, instance type set to t2.medium, and storage set to 8 GB.

As a result, an EC2 instance with the following Public IPv4 Address was created.

54.172.71.20

SSH

Connect to the launched EC2 instance via SSH. After connecting, set the root user password with the following commands.

sudo su
passwd

Installing Java

Install Java with the following commands.

apt-get update
apt install default-jre

Downloading Cantaloupe

Download Cantaloupe with the following commands.

apt install unzip
wget https://github.com/cantaloupe-project/cantaloupe/releases/download/v5.0.5/cantaloupe-5.0.5.zip
unzip cantaloupe-5.0.5.zip

Configuration

Edit the configuration file “cantaloupe.properties” with the following commands.

cd cantaloupe-5.0.5
# Copy the sample configuration file
cp cantaloupe.properties.sample cantaloupe.properties

Use a command such as vi cantaloupe.properties to specify the folder where images are stored.

cantaloupe.properties

FilesystemSource.BasicLookupStrategy.path_prefix = /home/ubuntu/images/

Starting Cantaloupe

java -Dcantaloupe.config=cantaloupe.properties -Xmx2g -jar cantaloupe-5.0.5.jar

Cantaloupe starts at the following URL.

http://54.172.71.20:8182/

Placing Images

Server-Side Work

Create a folder for storing images at /home/ubuntu/images using the ubuntu user.

su ubuntu
mkdir /home/ubuntu/images

Local Work

Use a notebook such as the following to download a pyramid tiled TIF locally.

https://zenn.dev/nakamura196/articles/ef6b0937e4e887

Then, if you downloaded converted.tif, upload the image to the server using a command like the following.

scp -i  /Users/xxx/Downloads/converted.tif ubuntu@54.172.71.20:./images/

Once the image is uploaded to the server, you can retrieve info.json at the following URL.

http://54.172.71.20:8182/iiif/3/converted.tif/info.json

A URL like http://54.172.71.20:8182/iiif/3/converted.tif also redirects appropriately.

Furthermore, by changing /3/ to /2/ as follows, you can retrieve the Image API v.2 info.json.

http://54.172.71.20:8182/iiif/2/converted.tif

Intermediate Summary

The minimum setup for launching Cantaloupe is now complete. HTTPS configuration and other tasks still need to be done separately, which I hope to cover in other articles.

Handling Large Images: Enabling max_pixels

For example, when targeting “Hyakki Yagyo-zu” (Night Parade of One Hundred Demons, held by the University of Tokyo General Library), the following URL shows that the image size is 79508 x 3082.

https://iiif.dl.itc.u-tokyo.ac.jp/iiif/2/hyakki%2Fimages%2Fhyakki.tif/info.json

This Pyramid Tiled TIFF can be downloaded from the following GitHub repository.

https://github.com/nakamura196/ptif_sample/blob/main/hyakki.tif

If this file is uploaded as-is and you attempt to retrieve the full-size image, the following error occurs.

http://54.172.71.20:8182/iiif/2/hyakki.tif/full/full/0/default.jpg

edu.illinois.library.cantaloupe.operation.IllegalSizeException: The requested pixel area exceeds the maximum threshold (max_pixels) set in the configuration.
	at edu.illinois.library.cantaloupe.operation.OperationList.validate(OperationList.java:826)
	at edu.illinois.library.cantaloupe.processor.Processor.validate(Processor.java:201)
	at edu.illinois.library.cantaloupe.resource.ImageRequestHandler.handle(ImageRequestHandler.java:399)
	at edu.illinois.library.cantaloupe.resource.iiif.v2.ImageResource.doGET(ImageResource.java:128)
	at edu.illinois.library.cantaloupe.resource.HandlerServlet.handle(HandlerServlet.java:97)
	at edu.illinois.library.cantaloupe.resource.HandlerServlet.doGet(HandlerServlet.java:35)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:791)

This occurs because max_pixels is set to 10,000,000 in cantaloupe.properties, and 79,508 x 3,082 = 245,043,656 exceeds max_pixels.

cantaloupe.properties

# Maximum number of pixels to return in a response, to prevent overloading
# the server. Requests for more pixels than this will receive an error
# response. Set to 0 for no maximum.
max_pixels = 10000000

You can remove the upper limit by setting max_pixels to 0, which is described later.

Setting max_pixels has the advantage of reducing server load, but it causes errors to be returned to users when they try to download full-size images in Universal Viewer and similar tools.

Therefore, let us explore a workaround using Cantaloupe’s Delegate Methods when the max_pixels threshold is exceeded.

Enabling Delegate Methods

Set delegate_script.enabled to true in cantaloupe.properties. Also specify delegate_script.pathname. Here, we assume the home directory of the ubuntu user.

cantaloupe.properties

# Enables the delegate script: a Ruby script containing various delegate
# methods. (See the user manual.)
delegate_script.enabled = true

# !! This can be an absolute path, or a filename; if only a filename is
# specified, it will be searched for in the same folder as this file, and
# then the current working directory.
delegate_script.pathname = /home/ubuntu/delegates.rb

Also copy the sample script as the ubuntu user.

cp /home/ubuntu/cantaloupe-5.0.5/delegates.rb.sample /home/ubuntu/delegates.rb

Updating the Script

Update the authorize method in the delegates.rb script as follows.

/home/ubuntu/delegates.rb

def authorize(options = {})
    max_pixels = 10000000
    request_uri = context['request_uri']
    full_size = context['full_size']

    if request_uri && request_uri.include?('/full/full/') && full_size
      width, height = full_size['width'], full_size['height']
      total_pixels = width * height

      if total_pixels > max_pixels
        {
          'status_code' => 301,
          'location' => "#{request_uri.split('/full/full/').first}/full/max/0/default.jpg"
        }
      else
        true
      end
    else
      true
    end
  end

With this, when a request containing /full/full/ in the URL exceeds the max_pixels value, it redirects to a URL containing /full/max/. This way, no error is returned and the image is downloaded at the size specified by max_pixels.

Handling Large Images: Disabling max_pixels

As an alternative to the above method, you can also disable max_pixels. Specifically, set max_pixels as follows.

cantaloupe.properties

# Maximum number of pixels to return in a response, to prevent overloading
# the server. Requests for more pixels than this will receive an error
# response. Set to 0 for no maximum.
max_pixels = 0

With this setting, depending on server specifications, you can use the server without worrying about max_pixels.

However, on the t2.medium instance used in this article, a java.lang.OutOfMemoryError: Java heap space error prevented downloading when specifying large width values.

On the other hand, with a higher-spec server, you do not need to worry about heap memory, but attempting to download the “Hyakki Yagyo-zu” at full size (URL containing /full/full/) resulted in a download failure as shown below.

This appeared to be caused by hitting the maximum JPEG size limit.

As a workaround, modifying the authorize method in the delegates.rb script as follows allowed downloading images with a specified maximum width or height.

/home/ubuntu/delegates.rb

def authorize(options = {})
    max_pixels = 65500
    request_uri = context['request_uri']
    full_size = context['full_size']
    if request_uri && request_uri.include?('/full/full/') && full_size
      width, height = full_size['width'], full_size['height']
      if width > max_pixels || height > max_pixels
        scale_factor = [max_pixels / width.to_f, max_pixels / height.to_f].min
        new_width = (width * scale_factor).round
        new_height = (height * scale_factor).round
        {
          'status_code' => 301,
          'location' => "#{request_uri.split('/full/full/').first}/full/#{new_width},#{new_height}/0/default.jpg"
        }
      else
        true
      end
    else
      true
    end
  end

However, with this setting, maxArea and maxWidth are not included in info.json, so accessing via a URL containing /full/full/ would result in an image smaller than the width and height stated in info.json, making this approach not entirely appropriate.

The advantage is that you do not need to specify max_pixels as the product of width and height, but it is necessary to consider whether this complies with the IIIF specification.

https://iiif.io/api/image/3.0/

Summary

This article introduced how to set up Cantaloupe.

It also presented an exceptional workaround using Delegate Methods for downloading large images. By leveraging Delegate Methods, it appears possible to achieve scenarios such as allowing only authenticated users to download large images.

Although this article introduced an unconventional usage, I hope to investigate more Delegate Methods use cases in the future.

I hope this article helps with building a IIIF image server using Cantaloupe.

Please note that this article may contain misunderstandings or inaccuracies, and any corrections or comments would be greatly appreciated.