CacooチームのKawabataです。本記事はヌーラバーブログリレー2022の8月4日分の記事です。
本記事では、自作のツールのバグのせいで悔しい思いをしつつも、改善につなげた話をします。あくまで個人的に作っているツールについての話なので、ヌーラボでの業務とは無関係です。記事を通して会社で働きながらも盆栽いじりのように個人開発を行うエンジニアライフの楽しさや大変さが伝われば幸いです。
ツールのバグによって、妻の仕事のデータを吹き飛ばしてしまいましたが、File System Access APIを使って改善につなげました。ヌーラボには個人開発倶楽部という部活があり、個人開発者同士で作ったものを紹介したり情報を共有したりしてお互いにモチベーションを高め合っています。
私は個人的にプレゼン動画作成ツールを作っています。簡単に説明すると「プレゼンテーションのスライドに音声を録音して、一本のプレゼンテーション動画を作成できる」というツールです。ツールについての詳細は以前に書いた記事『クライアントだけで完結するプレゼン動画作成ツールを作って公開しました』をご覧ください。
『Presen Vid』というタイトルで公開しております。
目次
悲劇は夜中に起こった
妻は仕事の資料作りのために我がツールを日常的に使ってプレゼン動画を作ってくれています。
ある日の夜中「作業してたらなんか動かなくなったんだけど(泣)」と叩き起こされました。
私「ちょっと見せて」
ブラウザ(Chrome)がフリーズしているようでした。とりあえずPC再起動を指示しました。
妻「PC再起動終わって開いたんだけど、過去のデータも全部見れない・・・」
私「ちょっとPC貸して」
このとき嫌な予感しかしていませんでした。
私「・・・ごめんデータ全部消えてるわ」
事態を収拾するために、下記のように状況の説明と謝罪の旨をお伝えしてなんとか事態は収拾しました。(※昔のプレゼンデータを一部使い回すことができたので、実際にはそこまでリカバー作業に時間はかかりませんでした。)
- データが全て消失しました
- 消失したデータについては復元できません
- 原因については現時点ではわかっておらず、調査させてください
- 恐れ入りますが、データ復旧は難しいのでまた同じ作業を行う必要があります
何が起こっていたかというと、ブラウザのストレージ領域に保存されていたプレゼンデータが全て無くなっていました。
なぜデータは消えたのか?
まず原因の究明を急ぎました。
音声や画像のデータはIndexedDBに保存していました。
妻の環境は次のようになっていました。
- PC全体のストレージの空き容量がほとんどなくなっていた
- ブラウザはChromeを使用していた
IndexedDBの容量制限にひっかかっていた
そこでIndexedDBの制限について調べてみると、次のことが書かれています。
MDNの『ブラウザーのストレージ制限と削除基準』のページでは下記のような記述がありました。
上限に達するとオリジン立ち退き (origin eviction) と呼ばれる処理を実行して、ストレージの総量が再び上限を下回るまで、オリジン全体に相当するデータを削除します。
また、web.devの『ウェブ用ストレージ』のページでは下記の記述がありました。
Chromiumベースのブラウザは、ブラウザの容量が不足すると、データの削除を開始します。最近最も使用頻度の低いデータから順に削除していき、ブラウザによるデータの使用量が制限以内に収まるまで続けます。
つまり、「IndexedDBはPCが容量不足になると自動で削除される」という制限にひっかかってしまったということでした。
ローカルにデータを保存する方法の代替案を用意する
データを保存先の代替実装が必要になってきました。サーバにデータを保存するのも検討しましたが金銭面でも工数面でもコストがかかりそうなので、あくまでローカルに保存する方針は変えませんでした。
File System Access APIを使ってみる
そこで、IndexedDBの代わりにFile System Access APIを使ってみることにしました。ローカルファイルの読み込みと書き込み、ディレクトリ構造へのアクセスなどができます。
このAPIを使う理由は以下です。
- IndexedDBのような容量制限がない
- 音声や画像などの大きいデータの保存に向いてそう
- サーバーにデータを保存するのはお金も工数もかかりそうなのでローカルに保存したかった
- まだ新しいAPIだがどうせなら個人開発で試してみたかった
そのAPIブラウザで使えるの?
各ブラウザの対応状況を見てみました。本記事執筆時点ではChrome、Edge、Safari、Operaでは対応しています。残念ながらFirefoxでは対応していません。なので、広く使われるプロダクトでの本格的な使用は時期尚早のようです。今回のような個人的なツールでは使ってみるのはありといったところでしょう。
https://caniuse.com/native-filesystem-api
File System Access APIでファイル読み書きのサンプルを作った
初めて触れるAPIだったので、サンプルを作ってみました。
簡易的なテキストエディタを作りました。入力したテキストがローカルのテキストファイルに同期しています。
サンプルのURLはこちらです。
https://file-system-access-api-sample.vercel.app/
ソースコードはこちらに置いておきました。
https://github.com/SatoshiKawabata/file-system-access-api-sample
ファイルの読み込み
ローカルファイルの読み込みサンプルコードです。showOpenFilePickerというAPIから読み込むファイルを選べます。これだけでファイルの読み込みができます。
const [fileHandle] = await showOpenFilePicker({}); const file = await fileHandle.getFile(); const text = await file.text();
ローカルのテキストファイルを読み込んでテキストエリアに挿入している様子です。
ファイルの書き込み
書き込みのサンプルコードです。
const [fileHandle] = await showOpenFilePicker({}); const writable = await fileHandle.createWritable(); await writable.write("書き込むテキスト"); await writable.close();
ローカルのテキストファイルに書き込んでいる様子です。左側のブラウザで入力した文字が、左側のテキストファイルに即時反映されています。
ファイルの新規作成
新規ファイル作成も可能です。showDirectoryPickerメソッドで、ファイル作成したいディレクトリを選択します。createフラグをtrueにすることによりファイルが存在しなければ新規作成されます。
const dirHandle = await showDirectoryPicker(); fileHandleRef.current = await dirHandle.getFileHandle("ファイル名", { create: true, // createフラグをtrueにする }); const writable = await fileHandleRef.current.createWritable(); await writable.write(text); await writable.close();
テキストファイルを新規作成している様子です。入力した文字列を新しくファイルとして保存しています。
ローカルファイルへの書き込み許可を求めるダイアログが出る
File System Access APIを使う上での注意点として、最初にファイルに書き込みを行うときに必ず許可を求めるダイアログが出ます。一度許可するとそのセッション中は再度要求されることはありませんが、別ページへの遷移やページのリロードすると再度許可を求められます。
これが結構煩わしいので、File System Access APIを使う上での大きなデメリットです。マイクやカメラへのアクセスと同様にドメインごとにアクセス権限を保持してくれると嬉しいのですが、セキュリティ上の理由からか今はそうなっていないです。
ユーザーのジェスチャー起因でAPIを呼ぶ必要がある
もう一つの注意点として、ローカルのファイルやディレクトリにアクセスするためのAPI(showOpenFilePicker、showDirectoryPickerなど)は、クリックなどのユーザーによるジェスチャーのイベントハンドラの中で呼び出す必要があります。そうでないと下記のエラーが吐かれてしまいます。
SecurityError: Failed to execute 'showDirectoryPicker' on 'Window': Must be handling a user gesture to show a file picker.
データ参照先を切り替えられるようにリファクタリング
データの読み書き部分がIndexedDBにしか対応していなかったので、自由に切り替えられるようにリファクタリングしました。データ参照部分をインターフェース化して、実装を切り替えるだけでデータの参照先をIndexedDBとFile System Access APIを切り替えられるようにしました。実際のインターフェースのコードはこちらです。
まだ使ってもらえていない…
本記事執筆時点ではまだ妻に使ってもらえていないです。。実際に使ってもらったら色々改善点が出てくると思うので、またその感想や改善点などをまとめた記事を書こうかと思います。
まとめ – 個人開発は楽しく学べる
IndexedDBの制限について何の対策も入れずに、画像や音声といった重いデータをむりやり保存していました。それが原因でデータが吹き飛んだのは、自分の落ち度なので反省して代替案を頑張って夜な夜な実装しました。
不具合報告を受けて、謝罪して、原因調査して、改善案考えて、実装して、デプロイして、と「趣味の割に仕事みたいなことしてんなー」と感じながらも、好き勝手に開発できる個人開発ならではの楽しさもあり、新しいAPIを試す学びにもなりました。データがローカルファイルとして保存されるので、データが吹き飛ぶことはもう起きないし、夜中に叩き起こされることもないでしょう!