Deleting Files in Subfolder and delete empty Folders - vb.net

I have to write a PlugIn (in VB.NET) that deletes files after a set amount of days. I've been using this Code to "delete" all files older then DataAge:
For testing purposes I use Log(file.Name) instead of file.Delete().
Dim directory As New IO.DirectoryInfo(.PluginXML.DeleteDirectory) //.PluginXML.DeleteDirectory = FilePath
For Each file As IO.FileInfo In directory.GetFiles()
If (Now - file.CreationTime).Days > .PluginXML.DataAge Then Log(file.Name) //.PluginXML.DataAge = FileAge
Next
How do I check for old files/empty folders in Subfolders from .PluginXML.DeleteDirectory?
I tried something like this, but it would delete the folders, instead of old files.
For Each folder As IO.DirectoryInfo In directory.GetDirectories()
If (Now - folder.CreationTime).Days > .PluginXML.DataAge Then Log(folder.FullName)
Next

One option is to use the overload of GetFiles (or even better*, EnumerateFiles) that accepts a SearchOption with which you can specify that it recurses all directories. For example:
For Each file As FileInfo In directory.EnumerateFiles("*.*", SearchOption.AllDirectories)
If (Now - file.CreationTime).Days > .PluginXML.DataAge Then Log(file.Name)
Next
The alternative is to create a recursive function that walks the directory tree using EnumerateDirectories:
Public Sub ProcessDirectory(directory as DirectoryInfo, dataAge as Integer)
For Each file As FileInfo In directory.EnumerateFiles()
If (Now - file.CreationTime).Days > dataAge Then Log(file.Name)
Next
For Each subDir as DirectoryInfo In directory.EnumerateDirectories()
ProcessDirectory(subDir, dataAge)
Next
End Sub
And then call the method using your base/root path
Dim rootDir As New DirectoryInfo(.PluginXML.DeleteDirectory)
ProcessDirectory(rootDir, .PluginXML.DataAge)
Though note that you will probably need to add error handling for when a directory is inaccessible, such as when an UnauthorizedAccessException or SecurityException is thrown.
* GetFiles loads all files into memory at once whereas EnumerateFiles returns an IEnumerable<FileInfo> that streams the results. This has benefits if you are dealing with directories that contain lots of files, especially if using the AllDirectories option.

Related

How do I add the file creation date filter?

How can I add the file creation date filter for files created today and tomorrow?
This part don't work and I need to add files created today and tomorrow:
.Where(Function(file) New FileInfo(file).CreationTime.Date = DateTime.Today.Date)
Dim pdfList As String() = _
Directory.GetFiles(sourceDir, "*.PDF").Where(Function(file) New
FileInfo(file).CreationTime.Date = _
DateTime.Today.Date)
For Each f As String In pdfList
'Remove path from the file name.
Dim fName As String = f.Substring(sourceDir.Length + 1)
' Use the Path.Combine method to safely append the file name to the path.
' Will overwrite if the destination file already exists.
File.Copy(Path.Combine(sourceDir, fName), Path.Combine(backupDir, _
fName), True)
Next
Dim folder As New DirectoryInfo(sourceFolderPath)
Dim files = folder.EnumerateFiles("*.pdf")
.Where(Function(fi) fi.CreationTime.Date = Date.Today)
For Each file In files
file.CopyTo(Path.Combine(backupFolderPath, file.Name))
Next
There are a few things to note here:
We are creating a DirectoryInfo and using it to get FileInfo objects. Given that you were creating a FileInfo for each file anyway, it's silly not to do this. That FileInfo object gives you the creation time, file name without the folder path and a method to copy the file.
We are calling EnumerateFiles rather than GetFiles. The latter returns an array containing all files, but we don't want all files in this case. If you're going to filter then enumeration is better, because it gets each file one by one, processes a file and discards it before getting the next. That's more efficient. In this case, we don't even call ToArray or ToList, so the files aren't actually retrieved until the loop and then they are retrieved and processed one by one.
The is no point using Date.Today.Date. Today has already had the time zeroed. Internally, Date.Today uses Date.Now.Date.

Identify some files in Specialized.StringCollection

I read the *.fileExt files using:
Dim tempList As New Specialized.StringCollection
         tempList.AddRange(My.Computer.FileSystem.GetFiles(path, subFolders, "*" & fileExt).ToArray)
When processing I find some files that were deleted and are invisible from the file explorer but that are read by .GetFiles, they have their original name preceded by: "~$"
Example: "C:.......\~$OriginalFileName.fileExt"
I tried to exclude files that have "~$" at the beginning of the name, but it's not a good solution since I can create and save a file with "~$" at the beginning of the name, then the code would avoid a file that was never deleted.
I want to continue using .AddRange and exclude deleted files, I see two options:
Prevent .GetFiles from taking those files.
Identify them in tempList to avoid processing them.
I can't get any of the two to work.
I guess there is a simple way to identify if a file is deleted or not... an attribute or flag that identifies it as "deleted"... but I don't know how to do it. It's possible? How I should proceed?
Some programs create a backup file while a file is being edited, and those files are named starting with "~$" and have the hidden attribute set. Sometimes those files get orphaned, perhaps because of a program crash, but you don't notice them because they are hidden.
To avoid getting hidden files, you can select only files which do not have the hidden attribute, something like this:
Option Strict On
Option Infer On
Imports System.IO
Module Module1
Sub Main()
Dim srcDir = "C:\temp\testfiles"
Dim fileExt = ".txt"
Dim tempList As New Specialized.StringCollection()
Dim selectedFiles = (New DirectoryInfo(srcDir)).GetFileSystemInfos("*" & fileExt).
Where(Function(fi) Not (fi.Attributes And FileAttributes.Hidden) = FileAttributes.Hidden).
Select(Function(fi) fi.FullName)
tempList.AddRange(selectedFiles.ToArray())
For Each s In tempList
Console.WriteLine(s)
Next
Console.ReadLine()
End Sub
End Module
I created some files in a directory, and one of them I named starting with "~$" and set its hidden attribute:
C:\temp\testfiles>dir
Directory of C:\temp\testfiles
21/10/2019 15:48 0 one.txt
21/10/2019 15:49 0 two.txt
21/10/2019 15:48 0 ~$one.txt
3 File(s) 0 bytes
C:\temp\testfiles>dir /a:h
Directory of C:\temp\testfiles
21/10/2019 15:49 0 ~$three.txt
1 File(s) 0 bytes
and got this output from the program:
C:\temp\testfiles\one.txt
C:\temp\testfiles\two.txt
C:\temp\testfiles\~$one.txt
N.B. You are probably making things difficult for yourself by using a Specialized.StringCollection() instead of a List(Of String).
This can help you:
tempList As New Specialized.StringCollection
tempList.AddRange((From sFile As String In My.Computer.FileSystem.GetFiles(path, subFolders, "*" & fileExt).ToList.AsEnumerable
Where sFile IsNot Nothing
Where sFile.IndexOf("~$") = -1).ToArray)

Visual Basic FileSystem.GetFiles

I'm making a console application and I want to see what files is in a folder
For Each foundFile As String In My.Computer.FileSystem.GetFiles("c:\users\zac\desktop\booked vehicle\requested\")
Console.WriteLine(foundFile)
Next
after using this code and find that the folder is empty I need an If statement that say's
If foundfile has no files then
tell user no files found
end if
but I don't know how to write this so Visual Basic understands.
Load the files into a variable then check the count.
Dim files = My.Computer.FileSystem.GetFiles("c:\users\zac\desktop\booked vehicle\requested\")
If files.Count = 0 Then
'tell user no files
Else
For Each file In files
Console.WriteLine(file)
Next
End If
FileSystem.GetFiles() returns a collection of file name strings. As OneFineDay showed, you can use the collection's Count property to know if any files were found.
The downside of using FileSystem.GetFile() is that it has to search the entire folder before then returning the entire list of filenames. If you are searching large folders and speed is an issue, consider using Directory.EnumerateFiles() instead. That way, you can output a message if no file was found, otherwise loop throuh the list of found files. For example:
Dim files = Directory.EnumerateFiles("c:\users\zac\desktop\booked vehicle\requested\").GetEnumerator()
If files.MoveNext Then
' files were found
Do
Console.WriteLine(files.Current)
Loop Until Not files.MoveNext
Else
' no files were found
End If
I personally would use Linq to accomplish this. It's very quick and efficient to use in this case. I put the Console.ReadLine() at the end to show the files, you can remove this if need to be. Also you can change the Console.WriteLine to not include the string (s) if you don't want to. The s was declared if you want to show the files and also to see if there are any files. As I said, this was for my viewing to see the files. Straight to the point!
Dim s As String = String.Join(Environment.NewLine, New DirectoryInfo("YOUR DIRECTORY").GetFiles().[Select](Function(file) file.Name).ToArray)
Console.WriteLine(If(s.Length > 0, s, "No files found!"))
Console.ReadLine()

Fastest subdirectory listing

I have an application where i want to get all subdirectories (first level) from a given path.
I use this code:
Dim di As New IO.DirectoryInfo(Application.StartupPath & "\Folderlist")
For Each d In di.EnumerateDirectories()
Console.WriteLine(d.ToString)
Next
As you can see, i just need the name of the folder, not some object with all info.
Is there any way i can speed this up?
Dim dirs() = Directory.GetDirectories(Application.StartupPath & "\Folderlist")
For Each dir In dirs
Dim parent = Path.GetFileName(dir)
Console.WriteLine(parent)
Next
MSDN -
Directory.GetDirectories
Gets the names of subdirectories (including their paths) in the
specified directory.
Path.GetFileName

Deleting temp folder files with vb.net

I’m trying to programmatically clear out my temps folder however it can't delete files that are in use, which would be okay as long as it would then delete all the files not in use. However my code basically demands either we delete all or none, not just those not in use.
Below is the code could anybody please tell me how I can work around this?
'Deletes files in temporary directory...
Dim MYDAYS_2 As String
Dim MYTEMPFOLDER As String = System.IO.Path.GetTempPath
'this reads the regestry key otherwise gives a default value in its place (365)...
MYDAYS_2 = Registry.GetValue("HKEY_LOCAL_MACHINE\SOFTWARE\AA", "HELLO", "365")
'Deletes all files older then the day specifyed in the variable MDAYS...
For Each file As IO.FileInfo In New IO.DirectoryInfo(MYTEMPFOLDER).GetFiles("*.*")
If (Now - file.CreationTime).Days > MYDAYS_2 Then file.Delete()
Next
You could use a simple try/catch:
For Each file As IO.FileInfo In New IO.DirectoryInfo(MYTEMPFOLDER).GetFiles("*.*")
If (Now - file.CreationTime).Days > MYDAYS_2 Then
Try
file.Delete()
Catch
' log exception or ignore '
End Try
End If
Next