Writing to files using File System Access API fails in Electron + Create React App

Viewed 1229

I have a create-react-app that reads and writes local files using File System Access API. When run in a browser (Chrome or Edge that support it), both reading and writing files work fine.

When the app is run in Electron, reading works but writing fails due to: Uncaught (in promise) DOMException: The request is not allowed by the user agent or the platform in the current context.

I am using the latest Electron (12.0.1) which uses the same Chromium (89.0.4389.82) as the one in my Chrome browser.

Below is the relevant code. The console log after requestPermission call shows true and granted in the browser and true and denied in Electron.

I tried disabling webSecurity when creating BrowserWindow, disabling sandbox with appendSwitch but nothing helped.

Is there a way to give Chromium in Electron more permissions?

If not, I am willing to handle file writing differently when in Electron. In that case, what to write in place of TODO in the code? Note that because it is a create-react-app, the fs module is not available.

export async function chooseAndReadFile() {
    const fileHandle = await window.showOpenFilePicker().then((handles) => handles[0])
    const file = await fileHandle.getFile()
    const contents = await file.text()
    return contents
}

export async function chooseAndWriteToFile(contents: string) {
    const fileHandle = await window.showSaveFilePicker()

    const descriptor: FileSystemHandlePermissionDescriptor = {
        writable: true,
        mode: "readwrite"
    }
    const permissionState = await fileHandle.requestPermission(descriptor)
    console.log(window.isSecureContext)
    console.log(permissionState)

    const writable = await fileHandle.createWritable()
    await writable.write(contents)
    await writable.close()
}

let isElectron = require("is-electron")
export async function chooseAndWriteToFileUniversal(contents: string) {
    if (isElectron()) {
        // TODO: Do what???
    } else {
        chooseAndWriteToFile(contents)
    }
}
1 Answers

Answering my own question, I finally used a solution with HTML download attribute, nicely described here. When this technique is used in Electron, it presents a file save dialog which is exactly what I want. When used in a browser, this technique just downloads the file without a prompt, so I will continue using File System Access API for browser environments.

Here is the code that handles downloading when running in Electron.

function download(filename: string, contents: string) {
    var element = document.createElement('a');
    element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(contents));
    element.setAttribute('download', filename);
    element.style.display = 'none';
    document.body.appendChild(element);

    element.click();

    document.body.removeChild(element);
}

let isElectron = require("is-electron");
export async function chooseAndWriteToFileUniversal(contents: string) {
    if (isElectron()) {
        download("data.txt", contents)
    } else {
        chooseAndWriteToFile(contents) // See the original question for implementation of this function
    }
}

Still, would be nice to know why/how is Chromium in Electron more restricted than in a normal Chrome or Edge browser, and if it can be changed.

Related