14

I am writing a directory monitoring utility in java(1.6) using polling at certain intervals using lastModified long value as the indication of change. I found that when my polling interval is small (seconds) and the copied file is big then the change event is fired before the actual completion of file copying.

I would like to know whether there is a way I can find the status of file like in transit, complete etc.

Environments: Java 1.6; expected to work on windows and linux.

skaffman
  • 398,947
  • 96
  • 818
  • 769
Krishna Kumar
  • 7,841
  • 14
  • 49
  • 61
  • 3
    I think that you got the "duplicate" aspect reversed. This was asked in 2009, the other was asked in 2013! – Mr Ed Feb 22 '15 at 14:19

6 Answers6

8

There are two approaches I've used in the past which are platform agnostic.

1/ This was for FTP transfers where I controlled what was put, so it may not be directly relevant.

Basically, whatever is putting a file file.txt will, when it's finished, also put a small (probably zero-byte) dummy file called file.txt.marker (for example).

That way, the monitoring tool just looks for the marker file to appear and, when it does, it knows the real file is complete. It can then process the real file and delete the marker.

2/ An unchanged duration.

Have your monitor program wait until the file is unchanged for N seconds (where N is reasonably guaranteed to be large enough that the file will be finished).

For example, if the file size hasn't changed in 60 seconds, there's a good chance it's finished.

There's a balancing act between not thinking the file is finished just because there's no activity on it, and the wait once it is finished before you can start processing it. This is less of a problem for local copying than FTP.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 3
    I tried the second approach on windows and found that file.length() gives me the final size (after copy completion) even when the copy is in progress. Any thoughts on this – Krishna Kumar Apr 15 '09 at 06:36
  • Yes, either use the same approach but with last-updated-time or use both size and time. But I'm surprised at your assertion - what is actually doing the copying? Explorer won't create the whole file up front. Bittorrent and other download accelerators are the only thing I've seen do that. – paxdiablo Apr 15 '09 at 06:45
  • I am doing manual copy/paste of file in explorer – Krishna Kumar Apr 15 '09 at 06:48
  • Interesting, when I do cut'n'paste of a big file in Explorer, it continuously increases the file size as it's copying. How big is your file? How long does it take to copy? – paxdiablo Apr 15 '09 at 07:03
  • @Pax yes, my version of WS03 does that too: the copied file instantaneously acquire the final size, even though it just started copying. And that's with really big files (more than 10Gb) – Varkhan Apr 15 '09 at 15:58
  • Maybe MSFT made this change in later Windows version. Best bet then is to use the last modification time (assuming that changes as the file gets copied). – paxdiablo Apr 16 '09 at 00:36
6

This solution worked for me:

File ff = new File(fileStr);

if(ff.exists()) {

    for(int timeout = 100; timeout>0; timeout--) {
        RandomAccessFile ran = null;

        try {
            ran = new RandomAccessFile(ff, "rw");
            break; // no errors, done waiting
        } catch (Exception ex) {
            System.out.println("timeout: " + timeout + ": " + ex.getMessage());
        } finally {
            if(ran != null) try {
                ran.close();
            } catch (IOException ex) {
                //do nothing
            }

            ran = null;
        }

        try {
            Thread.sleep(100); // wait a bit then try again
        } catch (InterruptedException ex) {
            //do nothing
        }
    }

    System.out.println("File lockable: " + fileStr +
                 (ff.exists()?" exists":" deleted during process"));
} else {
    System.out.println("File does not exist: " + fileStr);
}

This solution relies on the fact that you can't open the file for writing if another process has it open. It will stay in the loop until the timeout value is reached or the file can be opened. The timeout values will need to be adjusted depending on the application's actual needs. I also tried this method with channels and tryLock(), but it didn't seem to be necessary.

Doug Porter
  • 7,721
  • 4
  • 40
  • 55
Mr Ed
  • 5,068
  • 1
  • 19
  • 12
  • 3
    I believe this works in Windows / OS with manadatory lock only, but not Linux As at this point we are not sure did the writer acquire any lock ( java level and OS level) en.wikipedia.org/wiki/File_locking I run above code in Linux it seems the process and get read access to a file being written without any exception – vincentlcy Oct 09 '13 at 02:22
  • is there any way out for this in Linux – Akshay Kasar Dec 07 '17 at 12:55
4

Do you mean that you're waiting for the lastModified time to settle? At best that will be a bit hit-and-miss.

How about trying to open the file with write access (appending rather than truncating the file, of course)? That won't succeed if another process is still trying to write to it. It's a bit ugly, particularly as it's likely to be a case of using exceptions for flow control (ick) but I think it'll work.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
1

We used to monitor the File Size change for determine whether the File is inComplete or not.

we used Spring integration File endpoint to do the polling for a directory for every 200 ms.

Once the file is detected(regardless of whether it is complete or not), We have a customer File filter, which will have a interface method "accept(File file)" to return a flag indicating whether we can process the file.

If the False is returned by the filter, this FILE instance will be ignored and it will be pick up during the next polling for the same filtering process..

The filter does the following:

First, we get its current file size. and we will wait for 200ms(can be less) and check for the size again. If the size differs, we will retry for 5 times. Only when the file size stops growing, the File will be marked as COMPLETED.(i.e. return true).

Sample code used is as the following:

public class InCompleteFileFilter<F> extends AbstractFileListFilter<F> {

    protected Object monitor = new Object();
    @Override
    protected boolean accept(F file) {
    synchronized (monitor){

        File currentFile = (File)file;

        if(!currentFile.getName().contains("Conv1")){return false;}

        long currentSize = currentFile.length();
        try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
        int retryCount = 0;
        while(retryCount++ < 4 && currentFile.length() > currentSize){
            try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
        }

        if(retryCount == 5){
            return false;
        }else{
            return true;
        }
    }
    }
}
Fengzmg
  • 710
  • 8
  • 9
1

If I understood the question correctly, you're looking for a way to distinguish whether the copying of a file is complete or still in progress?

How about comparing the size of the source and destination file (i.e. file.length())? If they're equal, then copying is complete. Otherwise, it's still in progress.

I'm not sure it's efficient since it would still require polling. But it "might" work.

green_t
  • 2,997
  • 3
  • 21
  • 18
1

You could look into online file upload with progressbar techniques - they use OutputStreamListener and custom writer to notify the listener about bytes written.

http://www.missiondata.com/blog/java/28/file-upload-progress-with-ajax-and-java-and-prototype/

File Upload with Java (with progress bar)

Community
  • 1
  • 1
miceuz
  • 3,327
  • 5
  • 29
  • 33