-2

Is there a way to, in JavaScript running in the client's browser, access a local file for IO in following manner? The file is being appended to regularly (every ~25ms). We want to read every new line as it comes in. Preferably asynchronously (we get some kind of signal, or a callback executes) when anything is appended. Is this possible? If so, how?

My experience with Javascript is limited to D3, so a basic explanation on how or why/why not this is feasible would be appreciated. I can totally understand this not being possible due to security reasons.

EDIT: Just to make it clear, the file is on the clients computer. The file is just a text file. The process appending to it is a native process on the client's machine.

Ramith Jayatilleka
  • 2,132
  • 16
  • 25
  • You can probably find the answer by searching old stack overflow pages – Adrian May Oct 05 '14 at 02:21
  • Yes, you can use AJAX. – Spencer Wieczorek Oct 05 '14 at 02:22
  • 1
    local file in client machine? doesnt that pose a security threat? – Mithun Satheesh Oct 05 '14 at 02:24
  • That's what I thought. If necessary, we can have the client upload the file, but does that allow us to get what is appended after the upload? – Ramith Jayatilleka Oct 05 '14 at 02:25
  • @RamithJayatilleka The only solution I can really think of is creating an entire extension (like a google chrome extensions or firefox extensionn), which could communicate with your native process. – soktinpk Oct 05 '14 at 03:03
  • The file is a text file. I don't think this is a duplicate. All but two of the answers there are outdated. The reasonably dated ones point to FSO.js and the FileReader object, but those don't allow async callbacks when a file is appended to. – Ramith Jayatilleka Oct 05 '14 at 03:04
  • @RamithJayatilleka Processing from local html page or online html page ? – guest271314 Oct 05 '14 at 03:12
  • @guest271314 I was planning from online web page. If its necessary, I guess we can do this by local web page, but that kind of removes a lot of benefits. Then I might as well turn it into a native app. – Ramith Jayatilleka Oct 05 '14 at 03:13
  • @RamithJayatilleka A potential option could be to upload file _once_ , utilizing `input type=file` element , to a `textarea` (or `contentEditable`) element , then process contents of `textarea` , as required (when content modified) , via `ajax` ? – guest271314 Oct 05 '14 at 03:24
  • @RamithJayatilleka You need to get the user to open the file, themselves, I think... but once it's open, my solution should allow you to check the contents rather quickly (as long as it's not several megs... ...reading it all will be super-fast, because it's local, but comparing gigantic strings, to check for changes, dozens of times a second, might add some lag to the user-experience. – Norguard Oct 05 '14 at 06:36

1 Answers1

2

The answer is not really, no.

BUT there are some workarounds, depending on what you can get away with (supported browsers, etc)

When you grab a local file (I'm assuming you're using <input type="file">, rather than partially supported, unstandardized methods), you get a "change" event to subscribe to, but that change reflects the change in selected files, not a change in the contents of a particular file.

Moving beyond that, if you have a Blob or a File which inherits from Blob, you basically have a buffer full of data.

That buffer isn't going to dynamically update itself, based on changes elsewhere in the OS, nor does it support callback options to do so.

Alternatively, from a blob or a file, you have the option to use URL.createObjectURL( file ), which returns a temporary url, which references a file, rather than the content of the file.

That said, any change to that file might then be reflected from subsequent calls to that URI.
This isn't something I've built to test, yet.

There would be no callback; however, you could set up an interval-based heartbeat, to make an XHR call to that URI, and then compare the .responseText with the previous .responseText.

Again, no guarantees, but that's as close as I think you're going to get to an answer, right now (while still in the realm of plugin/extension-free solutions), and might just be worth an hour of working out a prototype, and a couple more, testing support (whether the link is live, rather than an initial representation, etc).

var input = document.querySelector("input[type='file']"), // client must open file, themselves
    fileHandle = "", // this will be the url for the local file
    content    = "", // this will be the latest contents of the file
    previousContent = ""; // this will be the previous contents

var ms = 1000,
    secs = 2,

    pollId; // will be the process ID for the polling (so you can stop checking for changes, later).

// wait for a file to be selected
input.addEventListener("change", function (evt) {

    var file = evt.target.files[0]; // grab the file object
    fileHandle = URL.createObjectURL(file); // create a url which points to the local file

    // create an interval to check for changes
    pollId = setInterval(
        getLatest(fileHandle, getCurrent, updateCurrent),
        secs * ms
     );
});

function updateCurrent (latest) {
    current = latest;
    doStuff(current);
    // the text has changed; it's been updated;
    // now call home or do something else
}

function getCurrent () { return current; }

function getLatest (url, getCurrent, onchange) {
    return function () {
        // this isn't actually calling the network
        // it's calling the local file
        var xhr = new XMLHttpRequest();
        xhr.open("GET", url);

        xhr.onload = function () {
            var latest = xhr.responseText;
            if (getCurrent() !== latest) { onchange(latest); }
        };

        xhr.send();
    };
}

You now have change polling, in supporting browsers.

Norguard
  • 26,167
  • 5
  • 41
  • 49
  • Thanks for this. I found some other methods (for a native app) that do what I want pretty easily, so for now I'm going along that direction, but I might prototype this out. – Ramith Jayatilleka Oct 05 '14 at 10:53