I'm writing an application in which I have to detect file changes inside a directory. New files, missing files, and so on. Each scan relative to the previous.
I'm currently doing a recursive scan to retrieve all the paths and comparing to the previous list in my database. The problem with this is that some clients require scanning for millions of files. This makes the system consume a lot of resources (cpu and memory). I'm even getting SystemOutOfMemory exceptions.
So I'm wondering if there's a better way to find those changes, maybe without a full scan.
Important note: I can't "monitor" for events since I have to detect changes that happened between scans, no matter if the system was running. I can't afford to lose a single change. So, unless it can catch changes made while it wasn't running I can't use FileSystemWatcher for this.
Without knowing how the Directory you are scanning is structured im assuming you have something simmilar to following example.
000/
001/
002/
With each directory having subdirectories simmilar to the top level directory.
This would allow you to build up an index like git does internally.
Storing identifiers for each tree item (subdirectories, Files) for easy comparrison.
You should then be able to counter the SystemOutOfMemoryException through splitting up the task into multiple sub tasks.
Regarding the runtime i see no possibility to lower it as one component of your system will always have to watch or compare each Item.
If when the files are written the modify date of the directories ist reliably updated you could use this as part of your comparrison.
StorageFolder.GetFilesAsync is incredibly slow:
~7 seconds for a folder with ~3500 files
Back in Windows Phone 8.0 Silverlight, I was able to get the content of the CameraRoll much faster (via the MediaLibrary):
<1 second for the same amount of files
Are there any possibilities to speedup GetFilesAsync up, or is there any alternative for getting files of a folder?
I need the photo files to immediately extract information such as the Geotag or DateTaken. You can see how fast they loaded with Silverlight in my app GeoPhoto - which I am now trying to port to UWP. I've already implemented caching (mapping geotag and DateTaken with the picture path), so I would only need the picture path for subsequent app starts. Photos not yet cached could then be displayed later (after the long GetFilesAsync-call), but it is important to give the user something he can interact with immediately after he launched the app.
I wonder if you've read this: https://www.suchan.cz/2014/07/file-io-best-practices-in-windows-and-phone-apps-part-1-available-apis-and-file-exists-checking/
Windows 8.1 - finally on Windows 8.1 the fastest method is the new StorageFolder.TryGetItemAsync method, but only by a slim margin when
comparing to other methods. The main benefit here is definitely the
simple code required without any Exception catching if the file does
not exists. The results for sync methods are similar to Windows 8
platform, if original synchronization context is not required, the
sync methods are a better choice.
In short, for checking if target file exists, on WP8 and WP8.1 Silverlight the fastest method is File.Exists, on Windows 8 and WP8.1
XAML you should use StorageFolder.GetFileAsync, and on Windows 8.1 use
the new method StorageFolder.TryGetItemAsync. Do NOT use the iteration
of StorageFiles returned from StorageFolder.GetFilesAsync on any
platform, it is terribly slow. Also if you don't need co continue on
the original thread, you can use the synchronous alternatives on WP8.1
XAML, Windows 8 and Windows 8.1 platforms.
or something like this?
StorageFolder.GetItemsAsync(UInt32, UInt32)
to fetch the first X number of files to give the user immediate feedback that you desire. After that load the rest.
https://msdn.microsoft.com/en-us/library/windows/apps/br227287.aspx
EDIT: Since my original answer seemed not to be helpful, I hope this one will be matching your problem.
I created a folder with ~4000 files, just for testing and used a Stopwatch for taking the time.
Just reading each item in the folder took:
System.IO.Directory.GetFiles(): 0.2 secs
Windows.Storage.StorageFolder.GetFilesAsync ~5.5 secs
Executing both mulitple times and in different order
I understand that this just gives you the file names as string, but depending on the library you use for reading the pictures, this might still help you.
Original Answer:
When you got the Path as string (e.g from ApplicationData.Current.LocalFolder.Path) you may use System.IO.Directory.GetFiles(string path). It does not return specific objects, but the path as string. You can use those with the static class System.IO.File.
It also allows you to pass a searchPattern, which allows you to use placeholders like * and ? and it's working synchronously, but retrieving files via this method works really quick.
I am developing a winform application that moves files between directories in a SAN. The app searches a list of files in directories with over 200,000 files (per directory) in the SAN's Directory, and then moves the found files to another directory.
For example I have this path:
\san\xxx\yyy\zzz
I perform the search in \zzz and then move the files to \yyy, but while I'm moving the files, another app is inserting more files into the xxx, yyy and zzz directories.
I don't want to impact the access or performance to other applications that use these directories.
The file's integrity is also a big concern, because when the application moves the files to \yyy directory another app uses those files.
All of these steps should run in parallel.
What methods could I use to achieve this?
How can I handle concurrency?
Thanks in advance.
Using the published .net sources, one can see how the move file operation works Here. Now if you follow the logic you'll find there's a call to KERNEL32. The MSDN documentation for this states
To perform this operation as a transacted operation, use the MoveFileTransacted function.
Based on that I'd say the move method is a safe/good way to move a file.
Searching through a large number of files is always going to take time. If you need to increase the speed of this operation, I'd suggest think out of side of the box to achieve this. For instance, I've seen cache buckets produced for both letters and dates. Using some sort of cache system, you could search for files in a quicker manner, say by asking the cache bucket for files starting with "a" for "these files like this starting with a" file, or files between these dates - etc.
Presumably, these files are used by something. For the purposes of this conversation, let's assume they are indexed in an SQL Database.
TABLE: Files
File ID Filename
-------------- ----------------------------
1 \\a\b\aaaaa.bin
2 \\a\b\bbbbb.bin
To move them to \\a\c\ without impacting access, you can execute the following:
Copy the file from \\a\b\aaaa.bin to \\a\c\aaaa.bin
Update the database to point to \\a\c\aaaa.bin
Once all open sessions have been closed for \\a\b\aaaa.bin, delete the old file
If the files are on the same volume, you could also use a hardlink instead of a copy.
I am developing a "dynamic shortcutting" application which creates special shortcut files which point to a registry entry rather than an actual file/executable. The registry entry contains the path of the desired file. I want to have a daemon running which watches the linked-to files and updates their registry entries if they are moved or renamed. Renamed I can handle using System.IO.FileSystemWatcher, but what is the best way to handle moved files?
I know this is beyond the basic functions of FSW (despite being a low-level file-system operation). The question is, what is the best way of doing it?
Most posts/articles I have read suggest ways that feel altogether "hacky", which basically involve looking for a delete followed by a create in a new place of a file, and connecting the two by file size, meta-data, time between the delete/create triggers, hashes, etc. This may well be the method I have to resort to, setting up FSWs on all drives. However, I am hoping there might be a better way.
Is it possible to either:
2.1. Listen in to the shell and "hear" move operations?
2.2 Or (even more radical) replace or add something to the shell move operation that either triggers some sort of event or performs the registry-updating task itself, precluding the need for the daemon?
I have a feeling that everyone is going to tell me that 1. is the only course, but I look forward to your suggestions. (answers in VB.NET preferred, but can translate from C# if necessary).
[I'm not sure if this should be appended as an "update" to my original post or posted as a separate answer]
To sum up (all two of) the answers plus my own experimenting (to try to give a definitive answer to this question):
It seems the only high-level (.NET) solution is to use the FileSystemWatcher which does not detect "move" out-of-the-box (despite it being a low-level command). The FSW approach is non-trivial, comparably resource-expensive, sloppy in places (i.e. using timers) and has its limitations and caveats. Nor does it provide a true reflection of "move" - it merely infers it from symptoms that are very likely to be a move (and have the same effect on the file-system in any case) but could theoretically be produced by non-move actions. Also, it appears you have to know what files you want to watch for moves in advance of the move happening, there's no-way of telling as it occurs.
On a lower-level (which would involve C++), one could hook API calls to get a faithful picture of when "moves" are called. This has the advantage that you don't have to decide to watch files in advance, and is also less resource-expensive than listening to "deletes" and "creates" and trying to compare them.
On a systems-programming level (which would involve C++ and could easily break your computer if you didn't know what you were doing) one could build a filesystem filter driver: this would take the concept of detecting moves to a truly anal level, detecting re-allocation of filesystem resources performed even without the kernel.
After some experimenting, here is the general structure of how the FileSystemWatcher approach (or at least the most obvious one to me) works, its quirks and its limitations. [no code atm, it's all pretty integrated into my application and I'm yet to optimise it, but I might add some snippets in here later].
The FileSystemWatcher method (to detect when files are moved or renamed):
.1. FileSystemWatchers.
You will need to create one FSW for each highest-level directory you want to monitor (for example, one for each writable logical drive).
.2. Renamed.
Straightforward renaming of the file is trivially handled.
.3. Moved.
This part is very far from trivial; it basically involves comparing files in three different scenarios.
3.0.1. Deciding if a deleted/moved-from file is the same as a created/moved-to file.
For determining whether a deleted and a created file are a match, filename is useless (can be changed during a move). You could use a mixture of file size and attributes like time created, or even a hash of the entire file. In my particular solution I only needed to watch the movement of specific files "registered" before load-time, so I was able to give these files a unique fingerprint as metadata that I could then use to compare files (this works fine in real-world scenarios, but is easy to break maliciously in testing, which disappoints me as a perfectionist.)
3.0.1.1. When to read filesize/attributes/take hash?
Before I came up with the static fingerprint idea, I was testing my code with a simple filesize + creation date validation check. I quickly realised though that I had to have a note of the filesize and creation date (or hash or whatever else you want to use) of the deleted file BEFORE it signals as "deleted", because you can't check the size of a file that doesn't exist. If (like me) you know the files you want to watch in advance, then you need to read in those values before you enable the FileSystemWatchers; you also need to listen for "change" events on those files to update the values of filesize and creation date, take a new hash etc. This then begs the question: what do you do if you DON'T know what files you are interested in watching to see if they move? What if you only know you are possibly interested in knowing if they've moved when they "delete"? That, unfortunately, is beyond me (it wasn't something I had to deal with.) Unless you can come up with a solution to this problem, there is zero point in continuing with the FileSystemWatcher approach. Furthermore, I would conjecture (though could very easily be wrong) that there is no high-level solution that will meet your needs. If you do however come up with a solution (please post it below/comment on this post/edit it in here on this post), I have made the rest of this compatible.
3.1. Scenario 1: Direct moving of the file itself.
Upon the "delete" of a specific file being detected, you need to start listening for a "create" of a congruous file. Rather than listening indefinitely for the matching "create" of a file that might just have been deleted (which in reality involves inspecting every file created in the directory), you can use a timer to start and stop a "listening" flag (practical, but from a purist point of view a little arbitrary), deciding that after e.g. 1000ms with no appropriately matching create it's likely there won't be one.
3.2.0. A common misconception.
A lot of people seem to be under the impression, after glancing at the docs, that moving or renaming a folder triggers a rename for all their subfiles and subfolders rather than a delete and a create. In actual fact what the docs say is:
If you cut and paste a folder with files into a folder being watched, the FileSystemWatcher object reports only the folder as new, but not its contents because they are essentially only renamed.
(i.e. only the top folder throws rename or create/delete and the subfiles/subfolders throw NOTHING). Meaning if you want to know when and where a certain file is moved, you have to listen out for each and every of its ascendent folders as well.
3.2.1. Scenario 2: Renaming of a containing folder.
In my solution, because I knew all the files I was watching, whenever one of my FileSystemWatchers reported a rename of a folder rather than a file (the portion of the string after the last "/" will contain no ".") I checked each of my watched files to see if their paths were in that directory and if so, changed the beginning of the filepath to the path of the new directory et voila!, I knew where my files had been moved to. If you do not now in advance what files you are looking for, then you will have to recursively search through everything in every folder that throws a "rename".
3.2.2. Scenario 3: Moving of a containing folder.
This one feels like a slap in the face: in order to build your move-detection routine, you have to be able to detect moves. Here folders will throw a "delete" followed by a "create". In my case the solution just recycles the techniques in 3.1 and 3.2.1: when a folder "delete" is detected, I check to see if it contains any of my watched files. If it does, I set a "listen" flag (and a timer to snuff it) and check the subdirectory path of my file in the old folder against every new folder "create" that is detected to see if it points to a file with the desired fingerprint. If it does, I now have the old and new paths of the file and have detected the move. If you don't know what files to watch for, you may have to validate folder moves by comparing size on disk and number of subfiles/subfolders between "deleted" folder and "created" folders to confirm a folder has moved first, then search the folder recursively for the files you're interested in.
3.3. FURTHER COMPLICATION: Cross-drive moving of large files.
This is a problem I fortunately didn't run into (because I was only comparing fingerprint metadata, and didn't need access to files); however moving large files between drives (which transfer in stages, triggering a create event then a series of change events) can cause real headaches.
3.3.1. Headache 1: The "create" fires when the destination file is incomplete.
This means comparing its size to a "deleted" file will produce a false negative. You can't even take a hash of the first part of the file to indicate to your program that this "might" be the deleted file, because the move operation will have the file access permissions locked down. You just have to try and tell if the created file might still be moving and wait for it to finish.
3.3.2. Headache 2: No sure way to "tell" that the created file is still being moved.
Some have suggested checking the file access permissions on the created file, but they might be indistinguishable from those on a file created and still in use by any random application. Others have suggested setting short time-limited listen flags for "changes" on the file, but again this is indistinguishable from a file being modified by an application. In fact if the file happened to be a log file constantly and rapidly being updated by some process, then waiting for "changes" to the file to timeout might never end.
3.3.3. Headache 3: (UNTESTED) possibly these sort of moves "delete" the file after "creating" the destination file*.
It makes sense that this would be the case, though I haven't tested it. [if anyone does know, feel free to edit (or delete) this section appropriately]
3.4. A philosophical quandry: are two identical files the same?
This is a very pedantic and arbitrary thought-experiment, but say you have two drives, each with an identical copy of File.txt. You run a batch file that deletes the copy on the first drive then immediately makes a copy of the file on the second drive into the same folder on the second drive and names it Copy of File.txt. Unless you are using fingerprints, your code will identify a delete and then a create of an identical file and be unable to distinguish what happened from a move (with renaming) of the file from the first drive to the second. The final state of the filesystem is identical in both cases so it shouldn't cause your application to behave unexpectedly, but art thou really content to call that a "move" based purely on isomorphism? (especially when you know the kernel sees it differently)?
Using high-level unrestricted api provided by C# - no, you cant. Use FileSystemWatcher.. On same drive operation of moving file is not "delete and create" - it's "rename".
If you can/want to go into lower-level, then you can hook MoveItem and MoveItems of IFileOperation shell's interface, and MoveFile from Kernel32.dll... It will work with most of apps, but require expansion for security rights for your application, that mostly unacceptable in corporative environment..
The task has two flaws that make it hard to implement: (a) move operation across the disks is actually a sequence of read/write operations followed by deletion rather than move. And during those read/write operations there can be some transformation of data in place ; and (b) moving can be performed not by just a shell.
What you can do is employ a filesystem filter driver to intercept file operations right when they take place. Then you need to detect the sequence of read and write operations performed by the same process over your file. I.e. if your code detects, that the file is read sequentially (NOTE: some copying tools can read the file in multiple threads in parallel) and then write similar blocks of data to the other file AND after reading everything the source file is deleted AND the complete file contents have been written to the other place, then you can guess that you have come over file move operation.
Bump & update: This may well be against the rules of StackOverflow, but I would like to point out to the many people landing on this page (and the myriad similar questions on SO) that I have started a feature request on MicroSoft UserVoice to add MOVE detection to FileSystemWatcher. The best solution in the long term, rather than trying to work around the problem, might be to petition MicroSoft to fix it. If you have come here because you too need a solution to this problem, please consider clicking here and voting for this feature.
I have been trying to figure out a way to streamline copying files from one drive (network or external in my case) to my main system drives. Short of creating a program that I will have to activate each time then choosing all the info in it, I have no really good design to bypass it.
I have been wondering if there was a way to intercept a straight-forward windows copy with a program to do it my way. The basic design would be that upon grabbing the copy (actually for this to be efficient, a group of different copies), the program would organize all the separate copies into a single stream of copies.
I've been wanting to do this as recently I have been needing to make backups of a lot of data and move it a lot as all my drives seem to be failing the past few months.
EDIT
For the moment, let's assume I am copying from a fully defragmented external drive (because I was). Let's also assume I am copying to a 99% defragmented drive (because I was). If I attempt to move every file at once, then the Windows copy method works great as it only needs to stream a contiguous file from one drive to the other (likely keeping the copy contiguous). This is basically best case, I need everything from there to move to here. So the copy method gets to grab the files in some algorithmicly logical order (typically by filename) and read then write the data to the new location.
Now for the reality of the situation, I instead copy a bunch of files from different locations (let's still assume fully defragmented as this is the case of my external), to different file locations on the second drive. I don't wish to wait for each one to finish before starting the next one, so I go through the folders picking and choosing what goes where and soon my screen is covered in COPY windows displaying the status of all the copies attempting to run at the same time. This presents a slight slowing down of the copy as a whole as each copy progresses independently of the others. That means that instead of a single contiguous file taking the total bandwidth available, all these files share the bandwidth approximately equally (this also adds in read and write delays to the disks as now the system has to grab a little from each file before circling back to the first). Still assuming best case scenario, this slower than if all the files were in one location and moved to one location.
What I want to do is intercept all the separate downloads and organize them into a list of single downloads that would happen one after another.
The only way you can intercept nicely is the FileSystemWatcher.
This is probably what you are looking for
detect Windows Explorer copy operation
You need to create a Shell Extension according to this post.