TEI/XMLファイルをS3互換のオブジェクトストレージでホストする
概要
TEI/XMLファイルをS3互換のオブジェクトストレージでホストする機会がありましたので、備忘録です。具体的には、mdx Iのオブジェクトストレージを対象にします。
背景
TEI/XMLファイルを読み込み、その内容を可視化するウェブアプリケーション(Next.js)を構築します。この時、ファイル数やサイズが小さい場合は、public
フォルダに格納していましたが、これらが大きくなった場合、別の場所でホストすることを考えました。
場所の選択肢は多々ありますが、今回はS3互換であるmdx Iのオブジェクトストレージを対象にします。
GUIを用いたオブジェクトストレージへのファイルアップロード
オブジェクトストレージへTEI/XMLファイルをGUI経由でアップロードする方法も多々あります。その中で、これまではCyberduckを使用する方法や、GakunNin RDMを使用する方法などを紹介しました。
一方、今回の事例では、TEI/XML以外のコンテンツをDrupalで管理していました。そこで、Drupalとオブジェクトストレージを接続し、ユーザはDrupalの操作で完結できるようにしました。
Drupalとオブジェクトストレージの接続
以下のモジュールを使用します。
インストール後、環境設定のページ/admin/config
から、S3 File System
を選択します。
そして、アクセスキーや秘密鍵を登録し、さらにS3のバケット名を登録します。
またAdvanced Configuration OptionsのCustom Host Settingsにおいて、https://s3ds.mdx.jp
を入力します。
これでオブジェクトストレージとの接続設定は完了です。
その後、各コンテンツタイプのフィード設定において、アップロード先として「S3 File System」を選択します。
また、今回はTEI/XMLファイルがアップロード対象となるため、「許可されている拡張子」として、xmlを入力します。
この結果、DrupalのGUIを介してアップロードしたTEI/XMLファイルが、mdx Iのオブジェクトストレージに格納されるようになりました。
(参考)DrupalのJSON:APIを用いたファイルの一括アップロード
TEI/XMLの初期登録にあたり、Pythonを用いた一括登録を行いました。JSON:APIを用いたファイルの一括アップロードの方法は、以下の記事などが参考になりました。
一例ですが、以下のようなスクリプトで実現できました。
import requests
import json
import os
from dotenv import load_dotenv
from glob import glob
from tqdm import tqdm
class ApiClient:
def __init__(self):
load_dotenv(override=True)
# DrupalサイトのURL(例)
self.DRUPAL_BASE_URL = os.getenv("DRUPAL_BASE_URL")
# エンドポイント(JSON:API)
# self.JSONAPI_ENDPOINT = f"{self.DRUPAL_BASE_URL}/jsonapi/node/article"
# 認証情報(Basic認証)
self.USERNAME = os.getenv("DRUPAL_USERNAME")
self.PASSWORD = os.getenv("DRUPAL_PASSWORD")
def login(self):
# ログインリクエスト
login_url = f"{self.DRUPAL_BASE_URL}/user/login?_format=json"
login_response = requests.post(
login_url,
json={"name": self.USERNAME, "pass": self.PASSWORD},
headers={"Content-Type": "application/json"}
)
if login_response.status_code == 200:
self.session_cookies = login_response.cookies
def get_csrf_token(self):
# CSRFトークンを取得
csrf_token_response = requests.get(
f"{self.DRUPAL_BASE_URL}/session/token",
cookies=self.session_cookies # ここでログインセッションを渡す
)
if csrf_token_response.status_code == 200:
# return csrf_token_response.text
# self.csrf_token = csrf_token_response.text
self.headers = {
"Content-Type": "application/vnd.api+json",
"Accept": "application/vnd.api+json",
"X-CSRF-Token": csrf_token_response.text,
}
else:
# raise Exception(f"CSRFトークン取得失敗: {csrf_token_response.status_code} {csrf_token_response.text}")
self.csrf_token = None
def upload_file(self, type, uuid, field, file_path, verbose=False):
url = f"{self.DRUPAL_BASE_URL}/jsonapi/node/{type}/{uuid}/{field}"
# ファイル名を取得
filename = os.path.basename(file_path)
# ファイルをバイナリモードで読み込む
with open(file_path, 'rb') as f:
file_data = f.read()
headers = self.headers.copy()
headers['Content-Type'] = 'application/octet-stream'
headers['Content-Disposition'] = f'attachment; filename="{filename}"'
# ファイルをアップロード
response = requests.post(url, headers=headers, cookies=self.session_cookies, data=file_data)
if response.status_code == 200:
if verbose:
print(f"ファイルアップロード成功: {filename}")
else:
print(f"ファイルアップロード失敗: {response.status_code} {response.text}")
すでに対象コンテンツが作成済みで、例えばfield_file
といったフィールドにファイルをアップロードする目的で使用することができます。
より適切な方法があるかと思いますが、以下のように使用できます。
client = ApiClient()
client.login()
client.get_csrf_token()
uuid = "cefa8076-4ddf-4c05-a03d-fcdebbf0c209"
file = "<ファイルのパス>"
content_type = "<コンテンツタイプ>"
field = "field_file"
client.upload_file(content_type, uuid, field, file)
なお、以下のような環境変数が必要です。
DRUPAL_BASE_URL=
DRUPAL_USERNAME=
DRUPAL_PASSWORD=
Next.jsからの利用
オブジェクトストレージにアップロードされたTEI/XMLファイルを、Next.jsなどのアプリケーションから読み込みます。
以下のライブラリを使用することができました。
具体的には、以下のような形で利用できました。
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
import { DOMParser, Document as XMLDocument } from '@xmldom/xmldom';
export const convertToXml = (xmlText: string): XMLDocument => {
const parser = new DOMParser();
const xml = parser.parseFromString(xmlText, 'text/xml');
return xml;
};
export const getXml = async (id: string): Promise<XMLDocument | null> => {
const client = new S3Client({
region: 'us-east-1', // サービスによって設定したりしなかったり
endpoint: process.env.S3_ENDPOINT || '',
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY_ID || '',
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY || '',
},
});
const command = new GetObjectCommand({
Bucket: process.env.S3_BUCKET || '',
Key: `xml/${id}.xml`,
});
const response = await client.send(command);
const content = await response.Body?.transformToString();
if (!content) {
return null;
}
return convertToXml(content);
};
以下のような環境変数とともに使用します。
S3_ACCESS_KEY_ID=
S3_SECRET_ACCESS_KEY=
S3_ENDPOINT=https://s3ds.mdx.jp
S3_BUCKET=
結果、以下のような構成を実現することができます。
今後の展望: LEAF-Writerとの接続
今回はDrupal(実際には、mdx Iのオブジェクトストレージ)にアップロードされたTEI/XMLファイルの編集環境は用意していませんが、例えば以下のLEAF-WriterのDrupalモジュールを使用することで、TEI/XMLファイルの編集と管理をCMS上で完結させられる可能性があります。
また、GakuNin RDMとLEAF-Writerを接続したプロトタイプとして、以下も参考になりましたら幸いです。
まとめ
今回、TEI/XMLファイルをS3互換のオブジェクトストレージでホストする一例について紹介しました。このような方法を採用する利点や欠点が考えられるため、用途に応じた構成を考える上で、本記事が参考になりましたら幸いです。
Discussion