Skip to content

Instantly share code, notes, and snippets.

@guest271314
Created February 15, 2025 05:32
Show Gist options
  • Save guest271314/0420c07ae2b92880b6ed42957b31b6ad to your computer and use it in GitHub Desktop.
Save guest271314/0420c07ae2b92880b6ed42957b31b6ad to your computer and use it in GitHub Desktop.
Write files with File System Access API without creating orphan .crswap files

The reason for using

await readable.pipeTo(writable)

is the .crswap file is automatically removed when the Promise fulfills.

Here's one way to write arbitrary volumes of data to the same file, without generating a bunch of orphan .crswap files.

The gist is

  • Use the File size to seek to the end of the file
  • Use exclusive mode option when constructing the FileSystemWritableFileStream to avoid creating a bunch of orphan .crswap files
  • Use WHATWG Streams pipeTo(fileSystemWritableFileStream)
  • Use FileSystemObserver to update File size to seek() to
var handle = await showSaveFilePicker({
  startIn: "downloads",
  suggestedName: "file.txt",
  id: "write",
});

let { size: currentSize } = await handle.getFile();

async function write(stream) {
  try {
    console.log(currentSize);
    const { resolve, promise } = Promise.withResolvers();
    // Doesn't observe created .crswap files
    const fso = new FileSystemObserver(
      async ([{ changedHandle, root, type }], record) => {
        const { size } = await changedHandle.getFile();
        resolve({
          type,
          size,
        });
        fso.disconnect();
      },
    );

    fso.observe(handle);
    const writable = await handle.createWritable({
      mode: "exclusive",
      keepExistingData: true,
    });
    await writable.seek(currentSize);
    await stream.pipeTo(writable, {
      preventClose: false,
    });
    const { type, size } = await promise;
    console.log(size, type);
    currentSize = size;
    fso.unobserve(handle);
  } catch (e) {
    console.log(e);
  }
}

for (let i = 65; i < 65 + 26; i++) {
  await write(new Blob([String.fromCodePoint(i)]).stream());
}
/*
0
fs.js:35 1 'modified'
fs.js:11 1
fs.js:35 2 'modified'
fs.js:11 2
fs.js:35 3 'modified'
fs.js:11 3
fs.js:35 4 'modified'
fs.js:11 4
fs.js:35 5 'modified'
fs.js:11 5
fs.js:35 6 'modified'
fs.js:11 6
fs.js:35 7 'modified'
fs.js:11 7
fs.js:35 8 'modified'
fs.js:11 8
fs.js:35 9 'modified'
fs.js:11 9
fs.js:35 10 'modified'
fs.js:11 10
fs.js:35 11 'modified'
fs.js:11 11
fs.js:35 12 'modified'
fs.js:11 12
fs.js:35 13 'modified'
fs.js:11 13
fs.js:35 14 'modified'
fs.js:11 14
fs.js:35 15 'modified'
fs.js:11 15
fs.js:35 16 'modified'
fs.js:11 16
fs.js:35 17 'modified'
fs.js:11 17
fs.js:35 18 'modified'
fs.js:11 18
fs.js:35 19 'modified'
fs.js:11 19
fs.js:35 20 'modified'
fs.js:11 20
fs.js:35 21 'modified'
fs.js:11 21
fs.js:35 22 'modified'
fs.js:11 22
fs.js:35 23 'modified'
fs.js:11 23
fs.js:35 24 'modified'
fs.js:11 24
fs.js:35 25 'modified'
fs.js:11 25
fs.js:35 26 'modified'
*/
// ABCDEFGHIJKLMNOPQRSTUVWXYZ
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment