I was performing enhancements on a video player that read its content at startup, and then would serve from that content on demand. The content, though loaded from the file system, was being put in place by another process. Since the primary application only scanned for content at startup, it would not detect the new content until after the scheduled daily reboot.
The application needed a different behaviour. It needed to detect when content was updated and rescan content accordingly. There are several ways that this could be done, with the application occasionally scanning its content being among the most obvious solutions. There are better solutions. The one I am sharing here is the File System Watcher. I’ll be looking at using the implementations for NodeJS and .NET.
The File System Watchers keeps track of files in specific paths and notifies an application when a change of interest occurs. Once could watch an entire folder or watch only specific files. If any files change, the application receives a notification.
Let’s consider how this feature is used in NodeJS first. You’ll need to import the file system object. The file system object has a function named watch that accepts a file path. The object that is returned is used to received notifications when an item within that path is created or updated.
const fs = require('fs')
const readline = require('readline');
const path = require('path');
var watcher;
let watchPath = path.join(__dirname, 'config');
console.log(`Watch path: ${watchPath}`);
watcher = fs.watch(watchPath)
watcher.on('change', (event, filename)=> {
console.log(event);
console.log(filename);
});
console.log('asset watcher activated');
When a configuration file is change, how that is handled depends on the logic of how your application works.
In the .Net environment there’s a class named FileSystemWatcher that accepts a directory name and a file filter. The file filter is the pattern for the file names that you won’t considered. Use *.* to monitor for any file. You can also filter for notifications of the file attributes changing. Instances of FileSystemWatcher exposes several events for different types of file system events.
RenamedDeletedChangedCreated
When an event occurs, the application receives a FileSystemEventArgs object. It provides three properties about the change that has occurred.
ChangeType– Type of event that occurredFullPath– The full path to the file system object affectedName– the name of the file system object affected
These should tell you most of the information that you need to know the nature of the change.
Whether in NodeJS or .Net, using the file system watcher provides a simple and efficient method for detecting when vital files have been updated. If you decide to add features to your application to ensure it is responsive to changes in files, you’ll want to use it in your solutions.
Find the source code for sample apps here
https://github.com/j2inet/FileSystemWatcherDemo
.Net Sample App
The .Net Sample App monitors the executable directory for the files content.txt and title.txt. The application has a title area and a content area. If the contents of the files are changed, the application UI updates accordingly. I made this a WPF app because the binding features makes it especially easy to present the value of a variable with minimal custom code. I did make use of some custom base classes to keep the app-specific code simple.
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.IO;
using System.Linq;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
namespace FileSystemWatcherSample.ViewModels
{
public class MainViewModel: ViewModelBase
{
public MainViewModel() {
var assemblyFile = new FileInfo(this.GetType().Assembly.Modules.FirstOrDefault().FullyQualifiedName);
var parentDirectory = assemblyFile.Directory;
Directory.SetCurrentDirectory(parentDirectory.FullName);
FileSystemWatcher fsw = new FileSystemWatcher(parentDirectory.FullName);
fsw.Filter = "*.txt";
fsw.Created += FswCreatedOrChanged;
fsw.Changed += FswCreatedOrChanged;
fsw.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.FileName;
fsw.EnableRaisingEvents = true;
}
void FswCreatedOrChanged(object sender, FileSystemEventArgs e)
{
var name = e.Name.ToLower();
switch (name)
{
case "contents.txt":
try
{
Content = File.ReadAllText(e.FullPath);
}catch(IOException exc)
{
Content = "<unreadable>";
}
break;
case "title.txt":
try
{
Title = File.ReadAllText(e.FullPath);
} catch(IOException exc)
{
Title = "<unreadable>";
}
break;
default:
break;
}
}
private string _title = "<<empty>>";
public string Title
{
get => _title;
set => SetValueIfChanged(() => Title, () => _title, value);
}
string _content = "<<empty>>";
public string Content
{
get => _content;
set => SetValueIfChanged(()=>Content, ()=>_content, value);
}
}
}
Node Sample App
The Node Sample App runs from the console. In operation, it is much more simple than the .Net application. When a file is updated it prints a notification to the screen.
const fs = require('fs')
const readline = require('readline');
const path = require('path');
function promptUser(query) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
return new Promise(resolve => rl.question(query, ans => {
rl.close();
resolve(ans);
}))
}
var watcher;
let watchPath = path.join(__dirname, 'config');
console.log(watchPath);
watcher = fs.watch(watchPath)
watcher.on('change', (event, fileName)=> {
console.log(event);
console.log(fileName);
if(fileName == 'asset-config.js') {
targetWindow.webContents.send('ASSET_UPDATE', fileName);
}
})
console.log('asset watcher activated');
var result = promptUser("press [Enter] to terminate program.")