Monoxerで画像や音声が付いた問題をインポートする仕組み

モノグサ開発チームの伊東です。今回は、MonoxerのBookをCSVファイルから作成する時に、画像や音声ファイル(以下メディアファイルと呼称)をシステムの内部でどのように扱っているかについて説明します。

Bookインポート機能とは

BookとはMonoxerにおける問題集のようなものです。MonoxerではBookを作成する主な方法として、大きく分けて2通りを提供しています。

  • WEB管理画面上で作成する方法
  • CSVファイルを作成しておいて、それをWEB管理画面から読み込ませる方法(Bookインポート機能)

WEB管理画面上で作成する方法は、ブラウザ上でマウスとキーボードをつかってBookを作成できるので比較的始めやすい方法です。

CSVファイルを作成する方法は、定められたフォーマットのファイルを書く必要がありますが、大量に問題を作成したい場合は、便利な方法です。特に同じ形式の問題をたくさん作る場合は楽になります。

【参考】

モノグサ管理マニュアル:Book(問題集)を作成する

※Bookインポート用のCSVファイルの例

・通常の英単語など

・音声を使うBook

・画像を使うBook

Bookインポート機能でメディアファイルを使いたい場合、CSVファイルとは別に、メディアファイルをアップロードする必要があります。 CSVファイルでBookを作成する前に、まずメディアファイルをアップロードしておきます。 CSVファイル中には、アップロード済みのファイル名を書くことで、ファイルを指定する仕組みにしています。

www.notion.so 使用するCSVファイルを①にインポートします。画像や音声を使う場合は②にインポートします。([.jpeg]と[.mp3]に対応しています) f:id:corp_monoxer:20211001171217p:plain

※ファイル一式をZIPファイル1つにまとめてアップロードするという方法も考えられますが、Bookを作成するユーザにとっては、ZIPファイルにまとめる手間がかかります。 CSVファイルは、Book完成までに何回か試しにインポートしてみて、修正を繰り返すのが普通ですので、その度にZIPファイルのまとめるのは面倒すぎます。 (実際のところサービスを実装する上でもファイルの処理が煩雑になって面倒そうです。)

このあたりの仕組みについてもう少し詳しく説明していきます。

アップロードされたファイルは、クラウド上のストレージ(オブジェクトストレージ)に格納されます。この際に、元のファイルの名前を使わず、新しいファイル名を作ってストレージに保存しています。なぜ元のファイル名を使わないのでしょうか?

名前を付け替えないと?

元のファイル名のままストレージ上に保存するというやり方だと、以下のような不都合があります。

  • 名前が容易に衝突する。そして上書きされると、既存Bookが壊れる。
  • 上書きされないような名前をユーザに付けてもらうのは不便
  • 中身が同じ別名のファイルが発生しうる

作成中のBookについては、一度アップロードしたファイルの差し替えが出来ることは望ましいのですが、以前に作成したBookには影響を及ぼさない方が良い。というわけで、名前を付け替えるのがよいです。

※インポート前に予測可能な規則を定めて、ユーザがアップロード時にファイル名を付与する方法も考えられます。例えば、エントリ番号を名前に付けて123.jpegなど。これはCSVファイル中でファイル名を書かなくても良くなるかもしれない(規則的なファイル名を暗黙的に指定できる)という利点がありますが、 以下のような欠点がありそうです。

  • ユーザがBookに対応したファイル名をBook毎につけ直すのは面倒
  • インポート前に毎回ファイルをアップロードし直す必要があるかもしれない
  • 実質的に同一のファイルを、システム上で同一視できない
  • 中身が同じファイルでもストレージ上で重複して保存される

さて、新しいファイル名の作り方です。すぐ思いつきそうなやり方としては、

  • 連番を振る方法
  • ランダムなファイル名を付ける方法

が考えられます。

これでファイル名が重複することは(ほぼ)ないのでよさそうな方法ですが、これらは事前に予測が難しいという問題点があります。これらをCSVファイル中にそのまま指定することは現実的ではありません。 インポートでメディアファイルを指定するためには、CSVファイルを用意する段階で、ファイルを特定できる情報(ファイル名)が予測可能であり、CSVファイル上で指定できる必要があります。 また、同じファイルを何度もアップロードした場合でも、別の名前のファイルとして、ストレージに保存されることになります。ストレージ容量が無駄になります。

同一オブジェクトの同一視と、重複排除の手法

オブジェクトストレージ上のオブジェクトのファイル名(以降、オブジェクト識別子)としては、中身が同じファイルを複数保存しないためには、実質的に同一とみなすべきオブジェクトの持つ特徴的な情報を識別子として利用するのがよいです。

今回はオブジェクトの本体のダイジェストハッシュ値を用いることにしました。つまり本体がバイト単位で完全一致した場合に同一オブジェクトとみなす方法です。

これにより、いつアップロードされたかにかかわらず、中身がファイルならば、システム内部で同じオブジェクトであるという扱いができるという利点があります。 これによりシステム内部で同一オブジェクト(画像や音声)は同一視されます。

  • オブジェクト識別子は、Bookの中で記憶度を検索するためのキーの一部としても利用されているため、同一のオブジェクトとその記憶度とを一対一対応させることができます
  • ストレージ上で重複排除されます

※実装ではハッシュ関数としてSHA256を用い、同一でないオブジェクト同士で識別子の衝突が発生することがないように留意しています。同様の手法はgit等で広く使われています(gitではSHA1)。

f:id:corp_monoxer:20211001171238p:plain
オブジェクトストレージ上の画像を入れるバケツ

インポート時のメディアファイルの特定方法

ユーザがCSVファイルを用意する時に、事前にアップロードしたときのメディアファイル名を、CSVファイル中に指定してもらいます。 そして、Bookインポート時に、CSVファイル中に指定されたファイル名を、ストレージ上のオブジェクト識別子に読み替えます。このために、ファイル名とオブジェクトの対応表を用意しています。この対応表は、メディアファイルをアップロードした段階で更新してあります。

f:id:corp_monoxer:20211001171248p:plain
ファイル名とオブジェクトの対応表

一度作ったBookの中の画像や音声は、サーバ上ではオブジェクト識別子(≒ストレージ上のファイル名)を使って表現されています。(元のファイル名とストレージ上のファイル名の対応表を参照して作成されます。) この仕組みにより、Bookインポート後のメディアファイルアップロード操作の影響は受けません。つまり同一ファイル名で、中身が異なるファイルをアップロードしても作成済みBookの画像や音声には影響を及ぼしません。

もし、画像や音声を変更したい場合は、別のファイル名でアップロードし直して、CSVファイル中でその別のファイル名を指定することで差し替えることができます。

メディアファイルのアップロード機能と、CSVファイルのインポート機能は、それぞれ独立しています。つまり、一度アップロードしたら、ブラウザをリロードしたりしても影響せず、その後のインポートで何度でも利用可能です。したがって、一度メディアファイルをアップロードすれば、あとは、CSVファイルだけ修正とインポートを繰り返すことができます。

もちろん、都度メディアファイルアップロードしてもらっても問題ありません。 上で説明したように、中身が同じファイルは1個だけオブジェクトストレージに保存されます。さらに、クラウドサーバでは一般的にアップロード方向の通信量には課金されないのでサービス運用側としては同じファイルを何度アップロードしてもらっても特にデメリットはないのです。

Bookインポート機能の内部処理のまとめ

1.メディアファイルのアップロード時

  • オブジェクト識別子を決定する(ダイジェストハッシュ値を計算)
  • オブジェクト本体をストレージに保存する
  • ファイル名とオブジェクトの対応表に、ファイル名とオブジェクト識別子を登録する

2.CSVファイルによるBookインポート時

  • ファイル名とオブジェクトの対応表を参照して、CSV中で指定されたファイル名から、オブジェクト識別子を取得する
  • Bookで使うオブジェクトをダウンロードするためのキーとして、オブジェクト識別子を使う
  • 記憶度を保存、検索するためのキーの一部として、オブジェクト識別子を使う

今回は、Bookインポートでメディアファイルを使う仕組みについて解説しました。私からは以上です。