Search code examples
javascriptfilereaderfileapi

Check if local file has changed


I have a web app, where the user can select a local file as input, using the html5 FileReader API. Is there any way I can check if the file has changed, that works in modern browsers?

Historically, this was possible in some browsers by polling the file object and comparing the File.lastModifiedDate (deprecated) or File.lastModified property, as outlined in this QA: Check if file has changed using HTML5 File API. However, the spec says that lastModifiedDate and other file data should be a snapshot of the file as it looked like when the users first selected it, so this should not work (and it seems like recent versions of most browsers indeed follow the spec now, making this hack unavailable).

I was hoping to be able to check for changes by reading the file. This kind of works, but as soon as the file is changed on disk, Chrome and Firefox throw an error saying DOMException: The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired. Is there any way around this?

This is what I tried:

let oldText

setInterval(function () {
  const fileObj = document.getElementById('myFileInput').files[0]
  const reader = new FileReader()
  reader.onload = evt => {
      const text = evt.target.result
      if (text !== oldText) {
        console.log("The file has changed!")
        oldText = text
      }
    }
    reader.readAsText(fileObj)
}, 1000)

...or simpler:

const fileObj = document.getElementById('myFileInput').files[0]
const reader = new FileReader()
reader.readAsText(fileObj)  // works
// now lets edit the file and try again
reader.readAsText(fileObj)  // fails

reader.readAsText() works as expected, until the file is changed, when it throws the error mentioned above. I guess this is a security measure of sorts, though I don't fully understand what it's trying to protect the user from. What can I do instead?


Solution

  • This will be possible again if/when the Native File System API is implemented in browsers. It will be partially enabled in Google Chrome 85, scheduled for release in October 2020.

    Unlike the FileReader API it requires a explicit user interaction, so you'd do something like this:

    myFileInput.addEventListener('change', async (e) => {
      const fh = await window.chooseFileSystemEntries()
      // fh is now a FileSystemFileHandle object
      // Use fh.getFile() to get a File object
    })