Skip to content

Cross-browser workaround that doesn't require an external host #348

@DavidBuchanan314

Description

@DavidBuchanan314

The gist of it is, stream the file to disk using OPFS, then finally "download" the file via a blob URI (so that it ends up as a regular user-accessible file in their Downloads folder).

const root = await navigator.storage.getDirectory();
const handle = await root.getFileHandle("file.txt", { create: true });

const writable = await handle.createWritable(); // returns a FileSystemWritableFileStream
await writable.write(new Uint8Array(10));
await writable.write(new Uint8Array(10));
await writable.close();

const file = await handle.getFile(); // nb, this is a "File" object, not a regular blob
const uri = window.URL.createObjectURL(file);
console.log(uri);

// should be able to use browser.downloads.download() from webextension context (untested)
const a = document.createElement("a");
a.style.display = "none";
a.download = "file.txt";
a.href = uri;
document.body.appendChild(a);
a.click();

// can't delete straight away! this is a race condition. not sure the best way to do this...
//await root.removeEntry("file.txt");

(This is just a PoC and not a productionized impl)

Pros

Cons

  • Does not work at all in Firefox private tabs :(
  • Only works in "secure" contexts.
  • OPFS has file size limits, but in practice these limits are quite generous: https://developer.mozilla.org/en-US/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria.
  • The total free disk space required is double the actual size of the file, because it must temporarily exist in both OPFS and the user's Downloads folder.
  • It's hard to decide when to delete the file from OPFS once you're done with it, because you can't tell when the final download is complete. A fixed delay would probably be Good Enough but it'd still be a race condition.

Unknowns

  • I haven't tested in Safari at all
  • I haven't yet tested for sure that it can handle larger-than-RAM files, but in principle it should.

Given the significant Firefox-private-tab limitation I'd understand if you have no interest in integrating this (or as part of the ponyfill), but I figured I'd leave it here for anyone else looking for alternative approaches.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions