概要
iPhone (iPod touch, iPad) アプリの GoodReader には、各種プロトコルによって複数のストレージでフォルダを同期する機能がある。それらのプロトコルのうち、WebDAV を使って Windows 7 で管理されているストレージとフォルダを同期する場合、GoodReader と Windows 7 とでファイル名のエンコーディングが異なるため、GoodReader で注釈を追加した PDF ファイルを Windows 7 に正しくアップロードすることができない。実験の結果、Windows 7 上で予めファイル名を変換しておくことでアップロードに成功することを確認した。
GoodReader の同期機能
GoodReader に内蔵された同期機能では、サーバストレージとの間でフォルダを同期させることができる。サーバストレージで追加されたファイルは自動的に GoodReader に取り込まれ、GoodReader 上で更新されたファイル(例: 注釈を追加した PDF)は、自動的にサーバに送り込まれる。サーバストレージとしては、Dropbox, SugarSync といったよく知られている公開サービスを使えるほか、任意の sFTP, FTP, WebDAV サーバ上で公開されているストレージにも対応している。
問題
GoodReader で、ファイル名に濁点を含むファイルを Windows 7 の IIS で構築した WebDAV サーバとの間で同期し、そのファイルに注釈を入れると、サーバ側ではファイルがふたつに増えてしまう(下図)。
このようになる理由は、「GoodReader が内部でファイル名を保持するのに、Mac OS X 標準の形式(UTF-16 NFD) を使っている」からだと考えられる。上記のファイル名がどのようなバイト列からなっているのかを Windows 7 上で確認すると、以下のようになる。
(元々あるファイル)自分のアタマで考えよう.pdf : 81ea 5206 306e 30a2 30bf 30de 3067 8003 3048 3088 3046 2e 70 64 66 (増えたファイル)自分のアタマて?考えよう.pdf : 81ea 5206 306e 30a2 30bf 30de 3066 3099 8003 3048 3088 3046 2e 70 64 66
「で」という文字(U+3067)が、「て」(U+3066)と濁点(U+3099)に分解されていることがわかる。
実験してみたところ、GoodReader は WebDAV サーバからファイルを受け取って、自らのストレージに保存したとき既に Unicode 正規化を行っていることがわかった。 単にダウンロードするだけであれば問題は顕在化しないが、WebDAV でアップロードが行うと、この正規化されたファイル名がそのまま使われてしまうことで、
- 正規化されたファイルが、新規ファイルとしてサーバに送り込まれる
- 正規化されていないファイルは GoodReader 上に存在しないことになり、同期に失敗する
という問題が起こることがわかった。この問題を以下では「Unicode正規化問題」と呼ぶ。Unicode の正規化に関しては「Unicode正規化とは」が詳しい。
解決方法
一番簡便な解決方法は、WebDAV サーバではなく、Dropbox や SugarSync をストレージとして使う方法である。これらのストレージとの同期には、 WebDAV プロトコルではなく、それぞれのサービスが提供している API を使っているようで、Unicode正規化問題は起こらない。しかし、巨大なフォルダ(10GB以上)を Dropbox や SugarSync と同期するのは、速度面でもコスト面でも避けたい。そこで、予め Windows 上でファイル名をすべて正規化しておくことを試みた。Java では、以下のようなコードで簡単に NFD で正規化された Unicode 文字列を得ることができるので、特定のフォルダを再帰的にスキャンし、すべてのファイル名を NFD で正規化してしまえばよい。
String filename = targetFile.getName(); String normalizedFilename = Normalizer.normalize(filename, Normalizer.Form.NFD); if (!filename.equals(normalizedFilename)) { // File#renameTo() でファイル名をnormalizedFilenameに変更する }
NTFSファイルシステムでは、NFD で正規化されたファイル名を持つファイルを問題なく保持できる。また、Windows 7 では、新しく作成されるファイルには NFC 正規化されたファイル名が与えられるが、既に存在するファイルを強制的に正規化することはないので、このようにユーザが意図的にファイル名を正規化しても、OS によって再度 NFC 正規化されることはない。