諸用でChrome拡張からファイルシステムを扱う事が出来ないかと思って調べたらちゃんとAPIが用意されてたんですが、例のごとく情報が少なくて使い方が良くわからなかったんで、公式ドキュメントやサンプルを読みながら理解した際の覚書です。
はじめに
以下の公式ドキュメントとサンプルを参考にしました。
細かいことはこちらを見ると良いと思います。
chrome.fileSystem API
GoogleChrome/chrome-app-samples/samples/filesystem-access/ - Github
そもそも、chrome.fileSystem APIはChrome拡張に向けて独自に用意された物ではなく、W3Cのドラフトにあった物をChromeに実装したものみたいですね。
File API: Directories and System - W3C
結局勧告まで至っておらず、これを実装しているのはChromeだけで、それがChrome拡張のAPIに転用されたという経緯っぽいですね。
chrome APIのドキュメントを見てもEntryに関するリファレンスが見当たらなかったのでどういう事かと思っていたんですが、MDNのドキュメントを見るのが良さそうです。
Entry - Web API インターフェイス | MDN
※追記
当初はChrome拡張から使えるのかと思ってたんですが、Chrome拡張(Extension)からは使えず、ChromeAppしか使えないみたいですね。ChromeExtensionとChromeAppの違いもよく分かってなかったので、改めて知識を整理したい…
準備
chrome.fileSystem APIを使うにはmanifest.jsonでpermissionを設定してやる必要があります。
"permissions": [
"...",
"fileSystem"
]
また、拡張機能のパッケージ化を行わないと、fileSystem APIは使えません。
そもそもChromeExtensionからはfileSystem APIを利用する事はできないようです。
fileSystem APIが使えるのはChromeAppのみのようです。
基本的な使い方
chrome.fileSystem.chooseEntryで、ダイアログを表示してユーザーにファイルやディレクトリを選択させる事ができます。
コールバックでユーザーが選択したファイル(ディレクトリ)のEntryが返ってくるので、よしなにしてやります。
(Entryのリファレンスはこちら)
Entryの情報を保存しておきたい場合(例えば、chooseEntryでユーザーに作業ディレクトリを選択させて、そのディレクトリに対して操作するというような形にしたい時など)は、retainEntryでEntryの情報をstring化した物をchrome.storageで保存しておき、適宜それを読みだしてrestoreEntryで再びEntryに復元するという手順を踏みます。
例:選択したディレクトリ内のファイルを列挙する
私が当初やりたかった事です。
公式のサンプルのコードを参照しながら流れを解説します。
https://github.com/GoogleChrome/chrome-app-samples/blob/master/samples/filesystem-access/js/app.js#L193-L203
https://github.com/GoogleChrome/chrome-app-samples/blob/master/samples/filesystem-access/js/app.js#L127-L152
// 1.
chooseDirButton.addEventListener('click', function(e) {
// 2.
chrome.fileSystem.chooseEntry({type: 'openDirectory'}, function(theEntry) {
// 3.
if (!theEntry) {
output.textContent = 'No Directory selected.';
return;
}
// 4.
// use local storage to retain access to this file
chrome.storage.local.set({'chosenFile': chrome.fileSystem.retainEntry(theEntry)});
// 5.
loadDirEntry(theEntry);
});
});
function loadDirEntry(_chosenEntry) {
chosenEntry = _chosenEntry;
if (chosenEntry.isDirectory) {
// 6.
var dirReader = chosenEntry.createReader();
var entries = [];
// 7.
// Call the reader.readEntries() until no more results are returned.
var readEntries = function() {
dirReader.readEntries (function(results) {
if (!results.length) {
textarea.value = entries.join("\n");
saveFileButton.disabled = true; // don't allow saving of the list
displayEntryData(chosenEntry);
}
else {
results.forEach(function(item) {
entries = entries.concat(item.fullPath);
});
readEntries();
}
}, errorHandler);
};
readEntries(); // Start reading dirs.
}
}
※説明の為に一部コメントを付加しています
- ボタンのクリックイベントを検知してchooseEntryしています。
- chooseEntryのタイミングでダイアログが表示されます。ここでは
{type: 'openDirectory'}
が指定されているので、ディレクトリ選択のダイアログが表示されます。 - ユーザーがダイアログ上で選択したディレクトリのDirectoryEntryが、コールバックの引数(
theEntry
)に渡されます。
theEntryをnullチェックして、何も選択されなかった場合(ダイアログでキャンセルされた場合)はreturnしています。 - chrome.storage.localにEntryを保存しておき、再起動時に再び読み取れるようにしておきます。(ファイルを列挙するのに必要ではありません)
- ディレクトリ内のEntryを読み取る関数に処理を渡しています。
- DirectoryEntryからcreateReaderを使ってDirectoryReaderを生成しています。
- DirectoryReaderからreadEntriesでディレクトリ内のEntryを読み出します。readEntriesのコールバックにEntryの配列が返ってくるので、結果が無くなるまで再帰的に呼び出しています。結果から各Entryのfullpathを取得して、結合したものをテキストエリアに表示しています。
まとめ
- FileSystem API自体はW3Cのドラフト(勧告には至らなかった)にあったものに則っている
- chrome.fileSystem.chooseEntryでダイアログを出してEntryを取得
- 取得したEntryに対してよしなに操作する
- Entryのstorageへの保存・復元はretainEntry, restoreEntryで行う
Chrome拡張の開発方法まとめ その1:概念編のほうもどうぞ。(その2いい加減書かなきゃな…)