📝

(メモ)Rust で WebAssembly 上でファイルを作成したい

に公開

あわせて読みたい

ファイルを読む話は別の記事に書きました。

https://zenn.dev/yutannihilation/articles/95303dddeb8044

WASI の場合

読む場合と同じく、filesystem APIが使えるはず。

Node.js の場合

読む場合と同じく、fsモジュールが使えるはず。

ブラウザ上の場合

やりたいことは、作成したファイルをユーザーにレスポンスとして返したいわけですが、主に2つやり方があるようです。

まずひとつは、WebAssembly からはデータを丸ごと返して、それを Blob に変換する、というやり方です。このやり方は、わかりやすいですが、ぜんぶメモリ上に乗せないといけないので、重いファイルにはあまり向かないかもしれません(具体的にどれくらいのファイルサイズならいいのかは、勘所がなくてよくわかりません...)。あと、web worker 上で処理するのを前提にするなら、worker のスレッドからメインスレッドへのコピーのコストもあります。

もう一つは、Origin Private FileSystem(OPFS)に書き出す、という手です。

https://zenn.dev/wasuwa/articles/77406ac862a867

重いファイルの場合はたぶんこっちが向いているでしょう。ただし、読む場合と同じく、WebAssemblyでは非同期処理を扱えないので、同期処理を使うことになります。たぶんこの API が使えるはずです。

https://developer.mozilla.org/en-US/docs/Web/API/FileSystemSyncAccessHandle/write

ChatGPT に書いてもらったコードによると(まだ試せてない)、web worker からはこんな感じで fileHandle を返して、

self.onmessage = async (event) => {
  ...
  const opfsRoot = await navigator.storage.getDirectory();
  const fileHandle = await opfsRoot.getFileHandle(filename, { create: true });

  const syncHandle = await fileHandle.createSyncAccessHandle();

  // syncHandle を介してデータを書き込む

  self.postMessage(fileHandle);
};

main thread 側では、URL.createObjectURL() で URL をつくってそれをダウンロードさせる、みたいな感じになるようです。

const worker = new Worker('worker.js');

worker.onmessage = async (event) => {
  const fileHandle = event.data;
  const file = await fileHandle.getFile();
  const url = URL.createObjectURL(file);

  // この URL をダウンロードさせる
};

(タイトルに「Rust で」って書いたのに Rust の話ぜんぜん出てこなくてすみません...)

Discussion