createHandoff

値(FileBlob、構造化複製できるものなら何でも)を、同じオリジンのページから次のページへ受け渡します。

ページ 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。値は使い切られています
}

補足

  1. 読むと消えます。 take() は、値を読むのと同じトランザクションの中でそれを消します。だからページを再読み込みしても同じファイルが開き直されませんし、古い ?open=local の URL は何も見つけられません。

  2. ふたつのタブが同時に勝つことはありません。 読み取りと削除がひとつのトランザクションに収まっているので、タブどうしが競っても、値が渡るのはきっかりどちらか一方です。

  3. put が決着するのはコミットの時点であって、書き込みを要求した時点ではありません。 値が確かなものになるのはトランザクションがコミットされてからで、ページはたいていその直後に移動してしまうからです。

  4. 失敗しても静かです。 IndexedDB がない、あるいは塞がれている場合(SSR、プライベートモード、サードパーティのフレーム)、putfalsetakenull で決着します。何かを渡そうと 試みた だけのページが、保存先がなかったせいで壊れてはいけません。

  5. ストアはバージョン 1 で作られます。 先にデータベースを開いたほうが作り、もう一方はすでにあるものを見つけます。

  6. 預けられるのは一度にひとつだけです。 これは受け渡しであって、待ち行列ではありません。2 度目の put は預かり中の値を上書きします。本当の保存が要るなら WebDB を使ってください。