はじめに
IIIFサーバーのCantaloupeでAzure Storageを使用している際、IIIF URLのidentifierと実際のAzure Storage上のファイルパスが異なる場合があります。本記事では、この問題をdelegate scriptを使って解決する方法を詳しく解説します。
課題
以下のようなファイル構造で画像を管理しているとします:
Azure Storage Container: mycontainer
├── images/
│ ├── collection1/
│ │ ├── item001/
│ │ │ └── item001_001.jpg
│ │ └── item002/
│ │ └── item002_001.jpg
│ └── collection2/
│ └── ...
しかし、IIIF URLでは以下のようにアクセスしたい:
https://example.com/iiif/3/collection1/item001/item001_001.jpg/info.json
この場合、IIIF URLのidentifier (collection1/item001/item001_001.jpg) と実際のAzure Storageのパス (images/collection1/item001/item001_001.jpg) が異なります。
AzureStorageSourceには、S3SourceのようなPATH_PREFIX設定が存在しないため、この問題を解決するためにはdelegate scriptを使用する必要があります。
解決方法
1. Docker Compose設定
services:
cantaloupe:
image: islandora/cantaloupe:main
environment:
CANTALOUPE_SOURCE_STATIC: AzureStorageSource
CANTALOUPE_AZURESTORAGESOURCE_ACCOUNT_NAME: ${AZURE_STORAGE_ACCOUNT_NAME}
CANTALOUPE_AZURESTORAGESOURCE_ACCOUNT_KEY: ${AZURE_STORAGE_ACCOUNT_KEY}
CANTALOUPE_AZURESTORAGESOURCE_CONTAINER_NAME: ${AZURE_STORAGE_CONTAINER_NAME}
CANTALOUPE_AZURESTORAGESOURCE_LOOKUP_STRATEGY: ScriptLookupStrategy # 重要
CANTALOUPE_DELEGATE_SCRIPT_ENABLED: "true"
CANTALOUPE_DELEGATE_SCRIPT_PATHNAME: "/opt/cantaloupe/delegates.rb"
volumes:
- "./delegates.rb:/opt/cantaloupe/delegates.rb:ro"
labels:
- "traefik.enable=true"
- "traefik.http.routers.cantaloupe.rule=Host(`example.com`)"
- "traefik.http.routers.cantaloupe.entrypoints=websecure"
- "traefik.http.routers.cantaloupe.tls=true"
- "traefik.http.services.cantaloupe.loadbalancer.server.port=8182"
restart: always
2. Delegate Script (delegates.rb)
開発・デバッグ版
最初はデバッグ出力を含む版で動作確認を行います:
##
# Delegate script for AzureStorageSource with ScriptLookupStrategy
#
class CustomDelegate
# コンテキストを保存するためのアクセサー
attr_accessor :context
##
# IIIF URLのidentifierをAzure Storage blob keyに変換
#
def azurestoragesource_blob_key
identifier = context['identifier'] if context
if identifier
blob_key = "images/#{identifier}"
puts "DEBUG: Input identifier: #{identifier}"
puts "DEBUG: Output blob key: #{blob_key}"
return blob_key
end
return nil
end
##
# 認証メソッド
#
def pre_authorize(options = {})
puts "DEBUG: pre_authorize called"
true
end
def authorize(options = {})
puts "DEBUG: authorize called"
true
end
##
# その他必要なメソッド
#
def extra_iiif2_information_response_keys(options = {})
{}
end
def extra_iiif3_information_response_keys(options = {})
{}
end
def redactions(options = {})
[]
end
def metadata(options = {})
nil
end
end
本番用クリーン版
動作確認後、本番環境用にデバッグ出力を削除:
##
# Complete delegate script for AzureStorageSource with ScriptLookupStrategy
#
class CustomDelegate
# コンテキストを保存するためのアクセサー
attr_accessor :context
##
# Cantaloupeから呼び出されるメイン機能
# IIIF URLのidentifierをAzure Storage blob keyに変換
#
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
##
# 追加のIIIF情報レスポンスキー(使用しない場合は空)
#
def extra_iiif2_information_response_keys(options = {})
{}
end
def extra_iiif3_information_response_keys(options = {})
{}
end
##
# 墨塗り機能(使用しない場合は空のリスト)
#
def redactions(options = {})
[]
end
##
# メタデータ追加(使用しない場合はnilを返す)
#
def metadata(options = {})
nil
end
end
トラブルシューティング
よくあるエラーと対処法
1. Cannot invoke "edu.illinois.library.cantaloupe.delegate.DelegateProxy.getAzureStorageSourceBlobKey()" because the return value is null
原因 : Delegate scriptが正しく読み込まれていない、または構文エラーがある
対処法 :
- Delegate scriptの構文を確認:
ruby -c delegates.rb attr_accessor :contextが定義されているか確認- コンテナログでdelegate scriptの読み込み状況を確認
2. undefined method 'context=' for CustomDelegate
原因 : attr_accessor :context が定義されていない
対処法 : クラスの先頭に attr_accessor :context を追加
3. undefined method 'pre_authorize' など
原因 : 必要なdelegate methodsが定義されていない
対処法 : 上記の完全なdelegate scriptを参考に、必要なメソッドを追加
4. class org.jruby.RubyHash cannot be cast to class java.lang.String
原因 : metadata()メソッドが不正な型を返している
対処法 :
- メタデータを使用しない場合は
nilを返す - 空のハッシュ
{}ではなくnilを返すことが重要
def metadata(options = {})
nil # 空のハッシュ{}ではなくnilを返す
end
デバッグ方法
- ログ監視 :
docker logs container-name -f
- 構文チェック :
ruby -c delegates.rb
- 段階的テスト :
最初はシンプルなdelegate scriptから始めて、徐々に機能を追加
動作確認
1. サービス起動
docker compose up -d
2. テストアクセス
# Info.json取得
curl https://example.com/iiif/3/collection1/item001/item001_001.jpg/info.json
# 画像取得
curl https://example.com/iiif/3/collection1/item001/item001_001.jpg/full/300,/0/default.jpg
3. ログ確認(デバッグ版使用時)
期待されるログ出力:
DEBUG: pre_authorize called
DEBUG: authorize called
DEBUG: Input identifier: collection1/item001/item001_001.jpg
DEBUG: Output blob key: images/collection1/item001/item001_001.jpg
まとめ
Cantaloupeのdelegate scriptを使用することで、IIIF URLと実際のストレージパスの差分を吸収できます。重要なポイント:
ScriptLookupStrategyの設定: AzureStorageSourceでdelegate scriptを使用するために必須attr_accessor :context: Cantaloupeがコンテキスト情報を設定するために必要azurestoragesource_blob_keyメソッド: パス変換のメインロジック- その他のdelegate methods : エラーを避けるために最低限必要
本記事で紹介した方法により、柔軟なファイル管理とIIIF URLの設計が可能になります。特に、既存のファイル構造を変更せずにIIIF対応を行いたい場合に有効です。