Imports System.IO
Imports System.Text.RegularExpressions
Imports DigitalData.Modules.Logging
''' File
''' 0.0.0.1
''' 11.10.2018
'''
''' Module that provides variouse File operations
'''
'''
''' NLog, >= 4.5.8
'''
'''
''' LogConfig, DigitalData.Module.Logging.LogConfig
''' A LogConfig object
'''
'''
'''
'''
'''
'''
'''
Public Class File
Private ReadOnly _logger As Logger
Private ReadOnly _logConfig As LogConfig
Private ReadOnly _invalidFilenameChars As String
Private ReadOnly _invalidPathChars As String
Private Const REGEX_CLEAN_FILENAME As String = "[\\/:""<>|\b\0\r\n\t]"
Private Const REGEX_CLEAN_PATH As String = "[:""<>|\b\0\r\n\t]"
Private Const FILE_NAME_ACCESS_TEST = "accessTest.txt"
Public Sub New(LogConfig As LogConfig)
_logConfig = LogConfig
_logger = LogConfig.GetLogger()
_invalidFilenameChars = String.Join("", Path.GetInvalidFileNameChars())
_invalidPathChars = String.Join("", Path.GetInvalidPathChars())
End Sub
Public Function GetCleanFilename(FileName As String) As String
_logger.Debug("Filename before cleaning: [{0}]", FileName)
Dim oCleanName As String = FileName
oCleanName = Regex.Replace(oCleanName, _invalidFilenameChars, String.Empty)
oCleanName = Regex.Replace(oCleanName, REGEX_CLEAN_FILENAME, String.Empty, RegexOptions.Singleline)
_logger.Debug("Filename after cleaning: [{0}]", oCleanName)
Return oCleanName
End Function
Public Function GetCleanPath(FilePath As String) As String
_logger.Debug("Path before cleaning: [{0}]", FilePath)
Dim oCleanName As String = FilePath
oCleanName = Regex.Replace(oCleanName, _invalidPathChars, String.Empty)
oCleanName = Regex.Replace(oCleanName, REGEX_CLEAN_PATH, String.Empty, RegexOptions.Singleline)
_logger.Debug("Path after cleaning: [{0}]", oCleanName)
Return oCleanName
End Function
'''
''' Adds fileversions to given filename `Destination` if that file already exists.
'''
'''
'''
Public Function GetVersionedFilename(Destination As String) As String
Try
Dim oFileName As String = Destination
Dim oFinalFileName = oFileName
Dim oDestinationDir = Path.GetDirectoryName(oFileName)
Dim oExtension = Path.GetExtension(oFileName)
Dim oVersionSeparator As Char = "~"c
Dim oFileVersion As Integer = Nothing
' Split Filename without extension at version separator to:
' - Check if file is already versioned
' - Get the file version of an already versioned file
'
' Example:
' test1.pdf --> test1 --> ['test1'] --> no fileversion
' test1~2.pdf --> test1~2 --> ['test1', '2'] --> version 2
' test1~12345~2.pdf --> test1~12345~2 --> ['test1', '12345', '2'] --> still version 2
Dim oFileNameWithoutExtension = Path.GetFileNameWithoutExtension(oFileName)
Dim oSplitFilename = oFileNameWithoutExtension.Split(oVersionSeparator).ToList()
' if file is already versioned, extract file version
' else just use the filename and set version to 1
If oSplitFilename.Count > 1 Then
Dim oVersion As Integer = 1
Try
oVersion = Integer.Parse(oSplitFilename.Last())
oFileNameWithoutExtension = String.Join("", oSplitFilename.Take(oSplitFilename.Count - 1))
Catch ex As Exception
' oFilenameWithoutExtension does NOT change
oFileNameWithoutExtension = oFileNameWithoutExtension
Finally
oFileVersion = oVersion
End Try
Else
oFileVersion = 1
End If
' while file exists, increment version
Do
oFinalFileName = Path.Combine(oDestinationDir, GetFilenameWithVersion(oFileNameWithoutExtension, oVersionSeparator, oFileVersion, oExtension))
_logger.Debug("Intermediate Filename is {0}", oFinalFileName)
_logger.Debug("File version: {0}", oFileVersion)
oFileVersion += 1
Loop While (IO.File.Exists(oFinalFileName))
_logger.Debug("Final Filename is {0}", oFinalFileName)
Return oFinalFileName
Catch ex As Exception
_logger.Warn("Filename {0} could not be versioned. Original filename will be returned!", Destination)
_logger.Error(ex)
Return Destination
End Try
End Function
Private Function GetFilenameWithVersion(FileNameWithoutExtension As String, VersionSeparator As Char, FileVersion As Integer, Extension As String) As String
If FileVersion <= 1 Then
Return $"{FileNameWithoutExtension}{Extension}"
Else
Return $"{FileNameWithoutExtension}{VersionSeparator}{FileVersion}{Extension}"
End If
End Function
'''
''' Removes files in a directory filtered by filename, extension and last write date
'''
''' The directory in which files will be deleted
''' Only delete files which are older than x days. Must be between 0 and 1000 days.
''' A filename filter which will be checked
''' A file extension which will be checked
''' Should the function continue with deleting when a file could not be deleted?
''' True if all files were deleted or if no files were deleted, otherwise false
Public Function RemoveFiles(Path As String, FileKeepTime As Integer, FileBaseName As String, Optional FileExtension As String = "log", Optional ContinueOnError As Boolean = True) As Boolean
If Not TestPathIsDirectory(Path) Then
Throw New ArgumentException($"Path {Path} is not a directory!")
End If
If Not Directory.Exists(Path) Then
Throw New DirectoryNotFoundException($"Path {Path} does not exist!")
End If
If FileKeepTime < 0 Or FileKeepTime > 1000 Then
Throw New ArgumentOutOfRangeException("FileKeepTime must be an integer between 0 and 1000!")
End If
Dim oUnableToDeleteCounter = 0
Dim oDirectory As New DirectoryInfo(Path)
Dim oDateLimit As DateTime = DateTime.Now.AddDays(FileKeepTime)
Dim oFiles As List(Of FileInfo) = oDirectory.
EnumerateFiles($"*{FileBaseName}*").
Where(Function(oFileInfo As FileInfo)
Return oFileInfo.Extension = FileExtension And oFileInfo.LastWriteTime < oDateLimit
End Function)
If oFiles.Count = 0 Then
_logger.Debug("No files found that match the criterias.")
Return True
End If
_logger.Debug("Deleting old files (Found {0}).", oFiles.Count)
For Each oFile As FileInfo In oFiles
Try
oFile.Delete()
Catch ex As Exception
If ContinueOnError = False Then
_logger.Warn("Deleting files was aborted at file {0}.", oFile.FullName)
Return False
End If
oUnableToDeleteCounter = oUnableToDeleteCounter + 1
_logger.Warn("File {0} could not be deleted!")
End Try
Next
If oUnableToDeleteCounter > 0 Then
_logger.Debug("Old files partially removed. {0} files could not be removed.", oUnableToDeleteCounter)
Else
_logger.Debug("Old files removed.")
End If
Return True
End Function
Public Sub MoveTo(FilePath As String, Directory As String)
Dim oFileInfo As New FileInfo(FilePath)
IO.File.Move(FilePath, Path.Combine(Directory, oFileInfo.Name))
End Sub
Public Sub MoveTo(FilePath As String, NewFileName As String, Directory As String)
IO.File.Move(FilePath, Path.Combine(Directory, NewFileName))
End Sub
'''
''' Tries to create a directory and returns its path.
''' Returns a temp path if `DirectoryPath` can not be created or written to.
'''
''' The directory to create
''' Should a write access test be performed?
''' The used path
Public Function CreateDirectory(DirectoryPath As String, Optional TestWriteAccess As Boolean = True) As String
Dim oFinalPath As String
If Directory.Exists(DirectoryPath) Then
_logger.Debug("Directory {0} already exists. Skipping.", DirectoryPath)
oFinalPath = DirectoryPath
Else
Try
Directory.CreateDirectory(DirectoryPath)
oFinalPath = DirectoryPath
Catch ex As Exception
_logger.Error(ex)
_logger.Warn("Directory {0} could not be created. Temp path will be used instead.", DirectoryPath)
oFinalPath = Path.GetTempPath()
End Try
End If
If TestWriteAccess AndAlso Not TestPathIsWritable(DirectoryPath) Then
_logger.Warn("Directory {0} is not writable. Temp path will be used instead.", DirectoryPath)
oFinalPath = Path.GetTempPath()
Else
oFinalPath = DirectoryPath
End If
_logger.Debug("Using path {0}", oFinalPath)
Return oFinalPath
End Function
Public Function TestPathIsWritable(DirectoryPath As String) As Boolean
Try
Dim fileAccessPath = Path.Combine(DirectoryPath, FILE_NAME_ACCESS_TEST)
Using fs As FileStream = IO.File.Create(fileAccessPath)
fs.WriteByte(0)
End Using
IO.File.Delete(fileAccessPath)
Return True
Catch ex As Exception
Return False
End Try
End Function
Public Function TestPathIsDirectory(Path As String) As Boolean
If Not Directory.Exists(Path) Then
Return False
End If
Dim oIsDirectory As Boolean = (System.IO.File.GetAttributes(Path) And FileAttributes.Directory) = FileAttributes.Directory
Return oIsDirectory
End Function
End Class