IIIF Image APIに準拠した画像配信を行うには、イメージサーバが必要です。本章では、オープンソースのIIIFイメージサーバとして広く利用されている Cantaloupe を取り上げ、導入から本番運用までの手順を体系的に解説します。

Cantaloupeの概要と特徴

Cantaloupe は、Javaで実装されたオープンソースのIIIF Image APIサーバです。IIIF Image API 2.x および 3.0 に対応しており、以下のような特徴を持っています。

  • 多様な画像フォーマット対応: JPEG、PNG、TIFF(ピラミッドTIFF含む)、JPEG2000、GIF等を処理可能
  • 複数のストレージバックエンド: ローカルファイルシステム、Amazon S3、Azure Blob Storage、HTTP経由の画像取得に対応
  • 動的な画像処理: リクエストに応じたリサイズ、回転、領域切り出し、フォーマット変換をリアルタイムに実行
  • Delegate Script: Ruby言語によるスクリプトで、認証・認可やパス変換などの高度なカスタマイズが可能
  • 管理画面(Control Panel): Webブラウザからキャッシュやサーバ状態を確認・管理
  • オーバーレイ機能: ウォーターマーク(画像・テキスト)の重畳

高解像度画像の配信には、ピラミッドTIFF または JPEG2000 形式が推奨されます。一方、比較的小さい画像(数百ピクセル程度)であれば、JPEGやPNGをそのまま配信することも可能です。

Dockerを使ったクイックスタート

Cantaloupeを最も手軽に試す方法は、Docker版を利用することです。IslandoraプロジェクトがDocker Hubにイメージを公開しています。

以下のリポジトリをクローンして、すぐに起動できます。

git clone https://github.com/nakamura196/docker_cantaloupe.git
cd docker_cantaloupe
docker compose up

起動後、http://localhost:8182/ にアクセスするとCantaloupeのトップページが表示されます。

プロジェクト内の iiif フォルダに画像ファイルを配置すれば、すぐにIIIF Image APIの動作を確認できます。たとえば sample.png を配置した場合、以下のようなURLで画像を操作できます。

#h#httittnppf::o/.//jllsoooccna2al0lh0ho,os2st0t:0:88118822//iiiii2if0f/0/3x2/2/s0sa0ammppllee..ppnngg//i2n0f0o,.2j0s0o,n200,200/full/0/default.jpg

Docker版はCantaloupeの動作確認やIIIF Image APIの学習に最適ですが、本番運用にはS3連携やHTTPS対応が必要になります。以降のセクションで順を追って説明します。

EC2での本格構築手順

AWSのEC2インスタンス上にCantaloupeを直接構築する方法を解説します。

インスタンスの準備

Ubuntu ベースのEC2インスタンスを作成します。推奨スペックは以下のとおりです。

項目推奨値
プラットフォームUbuntu
インスタンスタイプt2.medium 以上
ストレージ8GB以上(画像サイズに応じて増加)

Javaのインストールとダウンロード

Cantaloupeの実行にはJavaランタイムが必要です。

sudo apt-get update
sudo apt install default-jre

# Cantaloupeのダウンロードと展開
sudo 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
cd cantaloupe-5.0.5

設定ファイルの編集

サンプルの設定ファイルをコピーし、画像の格納パスを指定します。

cp cantaloupe.properties.sample cantaloupe.properties

cantaloupe.properties を編集して、画像ディレクトリを指定します。

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

起動

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

http://<サーバのIP>:8182/ にアクセスして起動を確認します。画像を /home/ubuntu/images/ に配置すれば、以下のようなURLでアクセスできます。

http://<IP>:8182/iiif/3/<>/info.json

systemdによるサービス化

上記のコマンドで直接起動した場合、SSH接続が切れるとサーバが停止してしまいます。本番運用では、systemdのサービスとして登録することを推奨します。

/etc/systemd/system/cantaloupe.service を作成します。

[Unit]
Description=Cantaloupe Image Server

[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/cantaloupe-5.0.5
ExecStart=/usr/bin/java -Dcantaloupe.config=cantaloupe.properties -Xmx2g -jar cantaloupe-5.0.5.jar
SuccessExitStatus=143

[Install]
WantedBy=multi-user.target

サービスを有効化して起動します。

sudo systemctl daemon-reload
sudo systemctl start cantaloupe
sudo systemctl enable cantaloupe  # OS起動時に自動起動
sudo systemctl status cantaloupe  # 状態確認

クラウドストレージ連携

本番環境では、画像をクラウドストレージに格納し、Cantaloupeから参照する構成が一般的です。ストレージのスケーラビリティとコスト効率を活用できます。

Amazon S3との連携

cantaloupe.properties で以下の設定を行います。

# ソースをS3に変更
source.static = S3Source

# S3の認証情報
S3Source.access_key_id = <アクセスキー>
S3Source.secret_key = <シークレットキー>
S3Source.region = <リージョン>

# バケット名の指定
S3Source.lookup_strategy = BasicLookupStrategy
S3Source.BasicLookupStrategy.bucket.name = <バケット名>

Docker版の場合は、環境変数で同様の設定を行います。

services:
  cantaloupe:
    image: islandora/cantaloupe:2.0.10
    environment:
      CANTALOUPE_SOURCE_STATIC: S3Source
      CANTALOUPE_S3SOURCE_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID}
      CANTALOUPE_S3SOURCE_SECRET_KEY: ${S3_SECRET_KEY}
      CANTALOUPE_S3SOURCE_REGION: ${S3_REGION}
      CANTALOUPE_S3SOURCE_BASICLOOKUPSTRATEGY_BUCKET_NAME: ${S3_BUCKET_NAME}
      CANTALOUPE_S3SOURCE_LOOKUP_STRATEGY: BasicLookupStrategy

S3を使う場合の利点として、画像ファイルへの直接アクセスを禁止しつつ(バケットのACLをデフォルトのまま)、Cantaloupe経由のアクセスのみを許可する構成が自然に実現できます。

画像のアップロードにはAWS CLIが便利です。

aws s3 sync ./images/ s3://<バケット名>/images/ --exclude "*/.DS_Store"

S3互換ストレージ(mdx.jp等)

S3互換のオブジェクトストレージにも対応可能です。mdx.jpのオブジェクトストレージを使う場合は、S3Source.endpoint(Docker版では CANTALOUPE_S3SOURCE_ENDPOINT)にエンドポイントURLを指定します。

CANTALOUPE_S3SOURCE_ENDPOINT=https://s3ds.mdx.jp

Azure Blob Storageとの連携

Microsoft Azure Blob Storageを使う場合は、Docker版での設定が簡便です。

# .env.azure.exampleを.envにリネームして設定
CANTALOUPE_AZURESTORAGESOURCE_ACCOUNT_NAME=<アカウント名>
CANTALOUPE_AZURESTORAGESOURCE_ACCOUNT_KEY=<アカウントキー>
CANTALOUPE_AZURESTORAGESOURCE_CONTAINER_NAME=<コンテナ名>
docker compose -f docker-compose-azure.yml up

なお、AzureStorageSourceにはS3Sourceのような PATH_PREFIX 設定が存在しません。IIIF URLのidentifierと実際のストレージパスが異なる場合は、後述するDelegate Scriptの azurestoragesource_blob_key メソッドでパス変換を行います。

HTTPS対応とリバースプロキシ設定

本番環境では、HTTPS対応が必須です。IIIFの仕様上、info.json に含まれるIDがHTTPSであることが多くのビューアで前提とされます。代表的な2つの方法を紹介します。

方法1: Nginx + Let’s Encrypt

EC2上でNginxをリバースプロキシとして配置し、Let’s Encryptで無料のSSL証明書を取得する方法です。

sudo apt install certbot nginx
sudo certbot certonly --standalone -d iiif.example.com

Nginx設定ファイル (/etc/nginx/sites-available/iiif.example.com) を作成します。

server {
    listen 80;
    server_name iiif.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name iiif.example.com;

    ssl_certificate /etc/letsencrypt/live/iiif.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/iiif.example.com/privkey.pem;

    location / {
        proxy_pass http://localhost:8182;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
sudo ln -s /etc/nginx/sites-available/iiif.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

方法2: Docker + nginx-proxyまたはTraefik

Docker環境では、nginx-proxyletsencrypt-nginx-proxy-companion の組み合わせ、またはTraefikを使う方法があります。Cantaloupeコンテナに環境変数 VIRTUAL_HOSTLETSENCRYPT_HOST を設定するだけで、自動的にSSL証明書の取得と更新が行われます。

services:
  cantaloupe:
    image: islandora/cantaloupe:2.0.10
    environment:
      VIRTUAL_HOST: iiif.example.com
      LETSENCRYPT_HOST: iiif.example.com
      LETSENCRYPT_EMAIL: admin@example.com
      # ...(S3設定等)

管理画面の活用

Cantaloupeには、Webブラウザからサーバの状態を確認・管理できるControl Panel(管理画面)が組み込まれています。

有効化

cantaloupe.properties で以下を設定します。

endpoint.admin.enabled = true
endpoint.admin.username = admin
endpoint.admin.secret = <安全なパスワード>

https://iiif.example.com/admin にアクセスすると、Basic認証を経て管理画面が表示されます。管理画面では、キャッシュの状態確認・クリア、設定値の確認、サーバ統計情報の閲覧が可能です。

本番環境ではパスワードを十分に強力なものにし、必要に応じてIPアドレスによるアクセス制限もNginx等で設定しましょう。

Delegate Scriptによる高度なカスタマイズ

Cantaloupeの最も強力な機能の一つが Delegate Script です。Rubyで記述されたスクリプトにより、リクエストごとに動的な制御を行えます。

有効化

delegate_script.enabled = true
delegate_script.pathname = /home/ubuntu/delegates.rb

サンプルスクリプトをコピーして編集を開始します。

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

info.jsonへの情報追加

extra_iiif3_information_response_keys メソッドを使って、info.json にライセンス情報やメタデータを追加できます。

def extra_iiif3_information_response_keys(options = {})
  {
    'rights' => 'http://example.org/license.html',
    'service' => [
      {
        '@id': 'https://example.org/auth/login',
        '@type': 'AuthCookieService1',
        'profile': 'http://iiif.io/api/auth/1/login',
        'label': 'Log In'
      }
    ],
    'exif' => context.dig('metadata', 'exif'),
    'xmp'  => context.dig('metadata', 'xmp_string')
  }
end

ストレージパスの動的変換

Azure Blob StorageでIIIF URLのidentifierと実際のファイルパスが異なる場合に、azurestoragesource_blob_key メソッドで変換を行います。

class CustomDelegate
  attr_accessor :context

  def azurestoragesource_blob_key
    identifier = context['identifier'] if context
    return identifier ? "images/#{identifier}" : nil
  end

  def pre_authorize(options = {})
    true
  end

  def authorize(options = {})
    true
  end
end

この場合、Docker Compose側で CANTALOUPE_AZURESTORAGESOURCE_LOOKUP_STRATEGY: ScriptLookupStrategy の設定が必要です。

大画像のリダイレクト処理

max_pixels の制限に引っかかるサイズの大きな画像に対して、自動的に縮小版にリダイレクトするロジックを実装できます。

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

オーバーレイ(ウォーターマーク)

画像にウォーターマークを重畳する機能もDelegate Script経由で制御できます。

def overlay(options = {})
  {
    'image' => 'https://example.com/watermark.png',
    'position' => 'bottom right',
    'inset' => 5
  }
end

positionrepeat にすれば、画像全面にパターンとして繰り返し表示されます。テキストのオーバーレイも cantaloupe.properties の BasicStrategy で設定可能です。

アクセス制御の設定

Cantaloupeでは、Delegate Scriptの authorize メソッドを使って、リクエストごとのアクセス制御を実装できます。

Bearer認証による全体制御

def authorize(options = {})
  header = context['request_headers']
               .select{ |name, value| name.downcase == 'authorization' }
               .values.first

  if header&.start_with?('Bearer ')
    token = header[7..header.length - 1]
    return true if token == "valid_token"
  end

  {
    'status_code' => 401,
    'challenge' => 'Bearer charset="UTF-8"'
  }
end

未認証ユーザへの品質制限

IIIF Authentication API と組み合わせて、未認証ユーザにはグレースケールや低解像度の画像を返し、認証済みユーザにのみフルカラー画像を提供するといった段階的なアクセス制御も実現できます。

def authorize(options = {})
  header = context['request_headers']
               .find { |name, value| name.downcase == 'authorization' }&.last
  request_uri = context['request_uri']

  # 認証済みユーザにはフルアクセス
  if header&.start_with?('Bearer ')
    token = header[7..-1]
    return true if token == "valid_token"
  end

  # 未認証ユーザにはグレースケール版にリダイレクト
  {
    'status_code' => 302,
    'location' => request_uri.sub('default', 'gray')
  }
end

まとめ

本章では、Cantaloupeの導入から本番運用に至るまでの主要なトピックを解説しました。

用途推奨構成
開発・検証Docker版 + ローカルファイルシステム
小中規模の本番運用Docker版 + S3/Azure + nginx-proxy (SSL)
大規模本番運用EC2 + S3 + Nginx + systemd + Delegate Script

Cantaloupeは設定ファイルとDelegate Scriptの組み合わせにより、シンプルな画像配信から高度なアクセス制御まで柔軟に対応できます。次章では、サーバの運用管理が不要なサーバーレスアーキテクチャによるIIIF画像配信について解説します。