How to watch file for new content and retrieve that content
Asked Answered
N

1

2

I have a file with name foo.txt. This file contains some text. I want to achieve following functionality:

  1. I launch program
  2. write something to the file (for example add one row: new string in foo.txt)
  3. I want to get ONLY NEW content of this file.

Can you clarify the best solution of this problem? Also I want resolve related issues: in case if I modify foo.txt I want to see diff.

The closest tool which I found in Java is WatchService but if I understood right this tool can only detect type of event happened on filesystem (create file or delete or modify).

Notability answered 19/6, 2014 at 7:55 Comment(8)
Java Diff Utils might helpLuckily
Check here #19481313Dank
It's impossible to get the difference only using this single file,unless you can make sure each modification is an appendant.Eisenberg
@J.Rush It's possible, Java Diff Utils actually does it. It bases file comparison on contents saved in different lists. Actually you can save the original file in a list and load it in another list whenever it's edited, then compare the two lists to see the differences.Luckily
@BackSlash,i think the lists you mentioned must be stored in other files,am i right?Eisenberg
@J.Rush No, I wasn't clear. Let's say we have myFile.txt. When the program is launched, it will retrieve the contents of myFile.txt and store them in a List<String>, let's call it originalFileContents. Every time myFile.txt gets modified, we load the file into another list, newFileContents. At this point we can do diff(originalFileContents, newFileContents) to find differences between the original file contents and the new ones.Luckily
@Luckily looks like very good library. I think if you add code example in answer it would a superb topic for stackoverflowNotability
@Notability Ok, I'm creating an exampleLuckily
L
2

Java Diff Utils is designed for that purpose.

final List<String> originalFileContents = new ArrayList<String>();
final String filePath = "C:/Users/BackSlash/Desktop/asd.txt";

FileListener fileListener = new FileListener() {

    @Override
    public void fileDeleted(FileChangeEvent paramFileChangeEvent)
    throws Exception {
        // use this to handle file deletion event

    }

    @Override
    public void fileCreated(FileChangeEvent paramFileChangeEvent)
    throws Exception {
        // use this to handle file creation event

    }

    @Override
    public void fileChanged(FileChangeEvent paramFileChangeEvent)
    throws Exception {
        System.out.println("File Changed");
        //get new contents
        List<String> newFileContents = new ArrayList<String> ();
        getFileContents(filePath, newFileContents);
        //get the diff between the two files
        Patch patch = DiffUtils.diff(originalFileContents, newFileContents);
        //get single changes in a list
        List<Delta> deltas = patch.getDeltas();
        //print the changes
        for (Delta delta : deltas) {
            System.out.println(delta);
        }
    }
};

DefaultFileMonitor monitor = new DefaultFileMonitor(fileListener);
try {
    FileObject fileObject = VFS.getManager().resolveFile(filePath);
    getFileContents(filePath, originalFileContents);
    monitor.addFile(fileObject);
    monitor.start();
} catch (InterruptedException ex) {
    ex.printStackTrace();
} catch (FileNotFoundException e) {
    //handle
    e.printStackTrace();
} catch (IOException e) {
    //handle
    e.printStackTrace();
}

Where getFileContents is :

void getFileContents(String path, List<String> contents) throws FileNotFoundException, IOException {
    contents.clear();
    BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(path), "UTF-8"));
    String line = null;
    while ((line = reader.readLine()) != null) {
        contents.add(line);
    }
}

What I did:

  1. I loaded the original file contents in a List<String>.
  2. I used Apache Commons VFS to listen for file changes, using FileMonitor. You may ask, why? Because WatchService is only available starting from Java 7, while FileMonitor works with at least Java 5 (personal preference, if you prefer WatchService you can use it). note: Apache Commons VFS depends on Apache Commons Logging, you'll have to add both to your build path in order to make it work.
  3. I created a FileListener, then I implemented the fileChanged method.
  4. That method load new contents form the file, and uses Patch.diff to retrieve all differences, then prints them
  5. I created a DefaultFileMonitor, which basically listens for changes to a file, and I added my file to it.
  6. I started the monitor.

After the monitor is started, it will begin listening for file changes.

Luckily answered 19/6, 2014 at 9:34 Comment(6)
you forgot to add realization of method getFileContentsNotability
@Notability Added it. I'm not very familiar with WatchService, a good starting point is the Oracle Tutorials page about it.Luckily
strange behaviour if at the moment of handling event was happened new eventNotability
if you will have more information - please add it here in futureNotability
Do you know if there is a way to get the diff without having to store old contents and new contents in memory and manually (or with some library) calculating the diff? In case the file is large, this looks not very efficient. I wonder if filesystems have some push API which can push only diffs, and if so, if there is a Java library on top of that.Purdum
@G.Urikh Not that I'm aware of. There surely are tools for diffing files which are memory efficient, but I don't think file systems expose an API for that purpose, although it's something they may use internally to efficiently store file changes. Likely you will have to write your own code to read small chunks from both files and compute the diff without being too heavy on memory (if you are lucky someone already did it and shared the code).Luckily

© 2022 - 2024 — McMap. All rights reserved.