Read local XML with JS

At the moment, due to the security policy Chromium can not read local files via ajax without --allow-file-access-from-files. But I currently need to create a web application where the database is a xml-file (in the extreme case, json), located in one dir with index.html. It is understood that the user can run this application locally. Are there workarounds for reading xml- (json-) file, without wrapping it in a function and change to js extension?

loadXMLFile('./file.xml').then(xml => {
    // working with xml
});

function loadXMLFile(filename) {
    return new Promise(function(resolve, reject) {
        if('ActiveXObject' in window) {
            // If is IE
            var xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
            xmlDoc.async = false;
            xmlDoc.load(filename);

            resolve(xmlDoc.xml);
        } else {
            /*
             * how to read xml file if is not IE?
             * ...
             * resolve(something);
             */
        }

    }
}

Accessing file: protocol at chromium using XMLHttpRequest() or <link> element without --allow-file-access-from-files flag set at chromium instance launch is not enabled by default.

--allow-file-access-from-files

By default, file:// URIs cannot read other file:// URIs. This is an override for developers who need the old behavior for testing.


At the moment, due to the security policy Chromium can not read local files via ajax without --allow-file-access-from-files. But I currently need to create a web application where the database is a xml-file (in the extreme case, json), located in one dir with index.html. It is understood that the user can run this application locally. Are there workarounds for reading xml- (json-) file, without wrapping it in a function and change to js extension?

If user is aware that local files are to be used by the application you can utilize <input type="file"> element for user to upload file from user local filesystem, process file using FileReader, then proceed with application.

Else, advise user that use of application requires launching chromium with --allow-file-access-from-files flag set, which can be done by creating a launcher for this purpose, specifying a different user data directory for the instance of chromium. The launcher could be, for example

/usr/bin/chromium-browser --user-data-dir="/home/user/.config/chromium-temp" --allow-file-access-from-files

See also How do I make the Google Chrome flag “--allow-file-access-from-files” permanent?

The above command could also be run at terminal

$ /usr/bin/chromium-browser --user-data-dir="/home/user/.config/chromium-temp" --allow-file-access-from-files

without creating a desktop launcher; where when the instance of chromium is closed run

$ rm -rf /home/user/.config/chromium-temp

to remove the configuration folder for the instance of chromium.

Once the flag is set, user can include <link> element with rel="import" attribute and href pointing to local file and type set to "application/xml", for option other than XMLHttpRequest to get file. Access XML document using

const doc = document.querySelector("link[rel=import]").import;

See Is there a way to know if a link/script is still pending or has it failed.


Another alternative, though more involved, would be to use requestFileSystem to to store the file at LocalFileSystem.

See

  • How to use webkitRequestFileSystem at file: protocol
  • jQuery File Upload Plugin: Is possible to preserve the structure of uploaded folders?
  • How to Write in file (user directory) using JavaScript?

Or create or modify a chrome app and use

chrome.fileSystem

See GoogleChrome/chrome-app-samples/filesystem-access.


The simplest approach would be to provide a means for file upload by affirmative user action; process the uploaded file, then proceed with the application.

const reader = new FileReader;

const parser = new DOMParser;

const startApp = function startApp(xml) {
  return Promise.resolve(xml || doc)
};

const fileUpload = document.getElementById("fileupload");

const label = document.querySelector("label[for=fileupload]");

const handleAppStart = function handleStartApp(xml) {
  console.log("xml document:", xml);
  label.innerHTML = currentFileName + " successfully uploaded";
  // do app stuff
}

const handleError = function handleError(err) {
  console.error(err)
}

let doc;
let currentFileName;

reader.addEventListener("loadend", handleFileRead);

reader.addEventListener("error", handleError);

function handleFileRead(event) {
  label.innerHTML = "";
  currentFileName = "";
  try {
    doc = parser.parseFromString(reader.result, "application/xml");
    fileUpload.value = "";

    startApp(doc)
    .then(function(data) {
        handleAppStart(data)
    })
    .catch(handleError);
  } catch (e) {
    handleError(e);
  }
}

function handleFileUpload(event) {
  let file = fileUpload.files[0];
  if (/xml/.test(file.type)) {
    reader.readAsText(file);
    currentFileName = file.name;
  }
}

fileUpload.addEventListener("change", handleFileUpload)
<input type="file" name="fileupload" id="fileupload" accept=".xml" />
<label for="fileupload"></label>

use document.implementation.createDocument("", "", null)

instead of new ActiveXObject('Microsoft.XMLDOM').

You can find the API through GOOGLE. Good luck.


If I understand correctly, the deliverable is intended to run locally so you will not be able to set any flags for local file access on a user's machine. Something I've done in a pinch is to pack it up as an executable with something like nw.js and keep the external data files. Otherwise, you're probably looking at loading as script using a JSON schema in a JS file.


I had a similar problem before. I solved by simply embedding the XML file into the HTML using PHP. Since the application is loaded locally from disk, size, cache etc. are not a concern.

If you're using Webpack, you can instead directly import the file using a loader like this or this, in which case the file is included into the resulting bundled javascript.


You can load XML through a string of text using DOMParser, Just load your file and parse the text using the .parseFromString. You could use an if statement containing (window.DOMParser) to check if the DOMParser is supported