FileAccessAPIは限られたブラウザでのみ使用可能。
ブツ
前回まで
情報源
- FileSystemAccessAPI - mozilla
- sql.jsのデータベースファイルをブラウザからFileSystemAccessAPIを使ってローカルに保存、復元を行ったメモ - qiita
FileSystemAccessAPI ブラウザ対応表 - mozillaをみると、FirefoxやIEは未対応。IEはもうサポートが切れたからいいとして、Firefoxが残念。Chrome、Edge, Opera, Safariなら使えるが、バージョンが高くないと使えない。
私のマシンはラズパイでブラウザがChromium 92。FileSystemAccessAPI ブラウザ対応表 - mozillaによるとChrome 86以降で使えるらしい。セーフ。というわけで使ってみる。
ポイント
- Chromium 86以降でないと使えない
- FileSystemAccessAPI - mozillaはボタンを押すなど、何らかのユーザ操作が必要であり自動化できない
- さらにそのファイルを操作するために許可を出すUI操作が必要
- さらにそのファイルを読み込むためのパスを指定するためのディレクトリ選択操作が必要
無断でローカルファイルを操作されてしまうセキュリティ的なリスクがある。それを回避するための措置として、ユーザ操作を必要としている。
えー、それじゃ自動化できないじゃん。ダウンロードしていたときより逆に手間がかかるようになってしまうのでは? とくに毎回パス選択とか面倒すぎるんですけど。そこだけはIndexedDBを使って保存すれば省略できるかもしれない。でも、それ以外のボタン押しと許可については省略できないっぽい。
stackoverflowによると、IndexedDBでディレクトリパスを保存することはできないらしい。IndexedDBは文字列でなく型として保存してくれるためイケるかと思ったが、セキュリティ的な理由でダメみたい。
とにかく実際にやってみる。
コード抜粋
<script src="lib/sql.js/1.6.2/sql-wasm.min.js"></script>
class Sqlite3DbFile {
#dirHandle = null
constructor() {
//this.PATH_WASM = `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.7.0`
this.PATH_WASM = `lib/sql.js/1.7.0`
this.name = 'users.db'
}
get DirHandle() { return this.#dirHandle }
async write() {
const dirHandle = await this.#getDirectoryPicker()
if (!dirHandle) return
try {
const fileHandle = await dirHandle.getFileHandle(this.name, {
create: true,
})
const writable = await fileHandle.createWritable()
await writable.write(this.db.export())
await writable.close()
} catch (e) {
console.error(e)
}
}
async read() {
const dirHandle = await this.#getDirectoryPicker()
if (!dirHandle) { return }
console.debug(dirHandle)
const fileHandle = await dirHandle.getFileHandle(this.name)
const file = await fileHandle.getFile()
const arrayBuffer = await file.arrayBuffer()
const dbAsUint8Array = new Uint8Array(arrayBuffer)
if (!this.SQL) {
//this.SQL = await initSqlJs({locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.6.2/${file}`})
this.SQL = await initSqlJs({locateFile: file => `${this.PATH_WASM}/${file}`})
}
this.db = new this.SQL.Database(dbAsUint8Array)
document.getElementById(`update`).disabled = false
return this.db
}
async #getDirectoryPicker() {
if (this.#dirHandle) { return this.#dirHandle }
try {
this.#dirHandle = await window.showDirectoryPicker()
return this.#dirHandle
} catch (e) {
console.error(e)
}
}
}
今回はなぜか1.7.0で実行できた。何が違うんだ? まいっか。動けばいいや。
これをボタンのクリックやらドラッグ&ドロップやらのイベントにより実行させる。
以下のような手順で、ファイルに上書きできたことが確認できる。
- DEMOページにアクセスする
ダウンロード
ボタンをクリックする- 2のZIPファイルを展開し
アドレス.db
ファイルをドラッグ&ドロップでロードする - ディレクトリ選択ダイアログが出る(ダウンロードした
users.db
ファイルが存在するローカルのディレクトリパスを選ぶ) - 読み取り許可ダイアログが出るので許可する
ytyaru
とhoge
のレコードが表示されるファイル上書き
ボタンをクリックする(ytyaru
レコードが追記される)- もう一度3を行う
ytyaru
とhoge
とytyaru
のレコードが表示される- 末尾に
ytyaru
レコードが追加されていたことが確認できた
ローカルにダウンロードしたファイルなのに、DEMOページのファイル上書き
ボタンに反応して、レコードが追記された。これが今回のポイント。なんと、ローカルファイルを読み書きできてしまった。
頑張ればネイティブアプリのようなことができるかも。けれど操作が面倒だし、使えるブラウザも限られているため、今の所は微妙かな。
ネイティブアプリをHTML,CSS,JSで作るならElectronなど専用のライブラリだかフレームワークだかを使って作ったほうがいいのだろう。たしかあれはブラウザのHTML描画エンジンやJavaScript実行エンジンやらをファイルに含めることでネイティブアプリとして稼働するようになるんだったかな?
前にらいうさんがExpo + NativeBaseでプロジェクト作成という記事をあげてらしたので参考になるかも。でもExpoはReactのことを知っている必要があるっぽい。私にはまだ無理ぽ。
というわけで、今はダウンロードやFileSystemAccessAPI - mozillaで妥協しておく。