createHandoff
値(File、Blob、構造化複製できるものなら何でも)を、同じオリジンのページから次のページへ受け渡します。
ページ A で利用者が選んだ File は、ページ B へは渡れません。URL には収まりませんし、直列化もできません。sessionStorage が受け取るのは文字列だけです。IndexedDB は構造化複製できる値をそのまま保存できるので、ページ A が値を預けて移動し、ページ B が取り出します。
API
createHandoff(options)
| パラメーター | 説明 | 型 | 既定値 |
|---|---|---|---|
dbName | データベースの名前。両側で揃えてください | string | 必須 |
storeName | オブジェクトストアの名前。最初に開いたときに作られます | string | 'files' |
key | 預かり中のただひとつの値を収めるキー | string | 'pending' |
戻り値
| メソッド | 説明 |
|---|---|
put(value) | 次のページのために値を預けます。預けられなかったときは false |
take() | 預かり中の値を取り出して消します。何も預かっていなければ null |
使用例
入口のページがアプリへファイルを渡す
import { createHandoff } from 'ranuts';
const handoff = createHandoff({ dbName: 'document-handoff' });
input.addEventListener('change', async () => {
await handoff.put(input.files[0]);
location.href = '/app?open=local';
});アプリが受け取る
import { createHandoff, queryFlag } from 'ranuts';
const handoff = createHandoff({ dbName: 'document-handoff' });
if (queryFlag('open')) {
const file = await handoff.take();
if (file) openDocument(file); // 再読み込みでは null。値は使い切られています
}補足
読むと消えます。
take()は、値を読むのと同じトランザクションの中でそれを消します。だからページを再読み込みしても同じファイルが開き直されませんし、古い?open=localの URL は何も見つけられません。ふたつのタブが同時に勝つことはありません。 読み取りと削除がひとつのトランザクションに収まっているので、タブどうしが競っても、値が渡るのはきっかりどちらか一方です。
putが決着するのはコミットの時点であって、書き込みを要求した時点ではありません。 値が確かなものになるのはトランザクションがコミットされてからで、ページはたいていその直後に移動してしまうからです。失敗しても静かです。 IndexedDB がない、あるいは塞がれている場合(SSR、プライベートモード、サードパーティのフレーム)、
putはfalse、takeはnullで決着します。何かを渡そうと 試みた だけのページが、保存先がなかったせいで壊れてはいけません。ストアはバージョン 1 で作られます。 先にデータベースを開いたほうが作り、もう一方はすでにあるものを見つけます。
預けられるのは一度にひとつだけです。 これは受け渡しであって、待ち行列ではありません。2 度目の
putは預かり中の値を上書きします。本当の保存が要るならWebDBを使ってください。