filefilesystemshtml5-filesystem

How do I use the File System Access API from Chrome using file:// url with no http server


I want to open an html file from Chrome on my computer, without an http server, and I want the JavaScript in the HTML file to be able read and write files in the local file system, and browse directories as well.

How do I do this using the File System API: https://wicg.github.io/file-system-access/ ?


Solution

  • File System API is not available currently in Chrome 85. For now you can launch the html file using a batch file which will locate and launch Chrome with the right appropriate command line options.

    Name the batch file the same name as the html file, and place the following in the batch file:

    @echo off
    
    setlocal
    set name=%~n0
    set here=%~dp0
    
    cd /d %here%
    set indexFile=%here%%name%.html
    if not exist "%indexFile%" set indexFile=%here%%name%.htm
    if not exist "%indexFile%" Echo Could not locate "%name%.htm" or "%name%.html" & pause & goto :eof
    
    get path to msedge.exe
    set exe=
    FOR /F "tokens=2* skip=2" %%a in ('reg query HKCR\MSEdgeHTM\DefaultIcon /ve') do set exe=%%b
    cls
    set exe=%exe:~0,-2%
    if defined exe goto exeFound
    
    rem get path to chrome.exe
    set exe=
    FOR /F "tokens=2* skip=2" %%a in ('reg query HKCR\ChromeHTML\DefaultIcon /ve') do set exe=%%b
    cls
    set exe=%exe:~0,-2%
    if defined exe goto exeFound
    
    start "" "%indexFile%"
    goto :eof
    
    :exeFound
    start "" "%exe%" --enable-experimental-web-platform-features --disable-web-security --no-proxy-server --no-sandbox --allow-file-access-from-files --allow-file-access  --no-default-browser-check --no-first-run --allow-running-insecure-content --enable-local-file-accesses --disable-extensions --user-data-dir="%temp%\%name%" --app="file:///%indexFile%"
    

    In the javascript you can make calls like this:

    Determine if API is available

    if (typeof showDirectoryPicker === 'undefined')
    

    Access directory or file

    const directoryHandle = await showDirectoryPicker()
    
    const fileHandle = await directoryHandle.getFileHandle(fileName)
    
    const str = await (await fileHandle.getFile()).text();
    

    See showOpenFilePicker(), showSaveFilePicker() and showDirectoryPicker() at https://wicg.github.io/file-system-access/ for more.

    Update:

    Tested with Chrome 88, and the batch file is not needed at all anymore.

    Update 2:

    How to disable web security when launch Chrome or Edge against a file: based url, from c#:

    private static void LaunchBrowser(string name, string indexFilePath)
    {
        var exe = GetBrowserExePath();
        if (string.IsNullOrEmpty(exe))
        {
            Console.WriteLine("Could not locate browser");
            return;
        }
    
        var userDataDir = Path.Combine(Path.GetTempPath(), name);
        var indexFile = new Uri(indexFilePath).AbsoluteUri;
    
        var parameters =
            " --disable-web-security" +
            " --no-default-browser-check" +
            " --no-first-run" +
            " --disable-extensions" +
            " --user-data-dir=" + "\"" + userDataDir + "\"" +
            " --app=" + "\"" + indexFile + "\"" +
            "";
        Process.Start(exe, parameters);
    }
    
    private static string GetBrowserExePath()
    {
        string exe = GetBrowserExePathWithKey("MdSEdgeHTM");
        if (string.IsNullOrEmpty(exe))
        {
            exe = GetBrowserExePathWithKey("ChromeHTML");
        }
    
        return exe;
    
        string GetBrowserExePathWithKey(string keyForChromeBaseBrowser)
        {
            var key = Registry.ClassesRoot.OpenSubKey(keyForChromeBaseBrowser);
            if (key != null)
            {
                key = key.OpenSubKey("DefaultIcon");
                if (key != null)
                {
                    var v = key.GetValue(string.Empty) as string;
                    var y = Regex.Match(v, @"(.*\.exe),");
                    if (y.Success)
                    {
                        var g = y.Groups;
                        if (g.Count > 1)
                        {
                            return g[1].Value;
                        }
                    }
                }
            }
    
            return string.Empty;
        }
    }