using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
namespace Sleis.Utility
{
///
/// Basic helper functions for dealing with files.
///
public static class FileUtility
{
public static string AppendToFileName(string filePath, string prependString)
{
string curName = Path.GetFileNameWithoutExtension(filePath);
return ChangeFileNameWithoutExtension(filePath, curName + prependString);
}
///
/// Change the file name present in filePath string to newFileName, without modifying the
/// parent directory or file extension.
///
public static string ChangeFileNameWithoutExtension(string filePath, string newFileName)
{
string curExt = Path.GetExtension(filePath);
string curDirectory = Path.GetDirectoryName(filePath);
return Path.Combine(curDirectory, newFileName + curExt);
}
///
/// Change the physical file extension of the input file on disk, and return the new file path.
///
public static string ChangeFileExtension(string filePath, string extension)
{
string curExt = Path.GetExtension(filePath);
if (string.Equals(curExt, extension, StringComparison.InvariantCultureIgnoreCase))
{
return filePath;
}
string newPath = Path.ChangeExtension(filePath, extension);
File.Move(filePath, newPath);
return newPath;
}
///
/// Return a full path given an input path relative to the currently executing assembly.
///
public static string PathFromExecutingAssemblyRelativePath(string relativePath)
{
return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
relativePath));
}
///
/// Move the input file to a new folder, keeping the same file name within the new folder.
///
public static string MoveFileToFolder(string originalFilePath, string moveToFolderPath)
{
string newFilePath = Path.Combine(moveToFolderPath, Path.GetFileName(originalFilePath));
try
{
File.Move(originalFilePath, newFilePath);
}
catch (Exception ex)
{
throw new IOException(string.Format("An error occurred attempting to move the file \"{0}\" to \"{1}\"",
originalFilePath, newFilePath), ex);
}
return newFilePath;
}
public static string SafeGetExistingFileDirectory(string fileOrDirectoryPath)
{
try
{
if (!Directory.Exists(fileOrDirectoryPath))
{
fileOrDirectoryPath = Path.GetDirectoryName(fileOrDirectoryPath);
return Directory.Exists(fileOrDirectoryPath) ? fileOrDirectoryPath : null;
}
else
{
return fileOrDirectoryPath;
}
}
catch (Exception)
{
return null;
}
}
public static string SafeGetFileName(string fileOrDirectoryPath)
{
try
{
return Path.GetFileName(fileOrDirectoryPath);
}
catch (Exception)
{
return null;
}
}
///
/// Return true if possibleFilePath represents a possible path to a file, which is either an existing file OR
/// a valid path to a non-existant file but valid parent directory.
///
public static bool IsValidPossibleFilePath(string possibleFilePath)
{
if (File.Exists(possibleFilePath))
{
return true;
}
if (!Directory.Exists(possibleFilePath) &&
(SafeGetExistingFileDirectory(possibleFilePath) != null))
{
return true;
}
return false;
}
///
/// Same as MoveFileToFolder(), but appends the current datetime ticks to the file name.
///
public static string MoveFileToFolderAppendDateTimeTicksToName(string originalFilePath, string moveToFolderPath)
{
string newFileName = Path.ChangeExtension(Path.GetFileNameWithoutExtension(originalFilePath) + "_" + DateTime.Now.Ticks.ToString(),
Path.GetExtension(originalFilePath));
string newFilePath = Path.Combine(moveToFolderPath, newFileName);
try
{
File.Move(originalFilePath, newFilePath);
}
catch (Exception ex)
{
throw new IOException(string.Format("An error occurred attempting to move the file \"{0}\" to \"{1}\"",
originalFilePath, newFilePath), ex);
}
return newFilePath;
}
///
/// Compare two input file versions.
///
public static int CompareFileVersions(FileVersionInfo version1, FileVersionInfo version2)
{
int rtnVal = version1.FileMajorPart - version2.FileMajorPart;
if (rtnVal != 0)
{
return rtnVal;
}
rtnVal = version1.FileMinorPart - version2.FileMinorPart;
if (rtnVal != 0)
{
return rtnVal;
}
rtnVal = version1.FileBuildPart - version2.FileBuildPart;
if (rtnVal != 0)
{
return rtnVal;
}
return 0;
}
///
/// Return a "cleansed" version of fileName that has any invalid file characters
/// replaced by replacementChar.
///
public static string ReplaceInvalidFilenameChars(string fileName, char replacementChar)
{
char[] invalidChars = Path.GetInvalidFileNameChars();
int index = fileName.IndexOfAny(invalidChars);
if (index < 0)
{
return fileName; // No invalid characters
}
StringBuilder sb = new StringBuilder(fileName);
do
{
sb[index] = replacementChar;
if (index == (fileName.Length - 1))
{
break;
}
index = fileName.IndexOfAny(invalidChars, index + 1);
} while (index > 0);
return sb.ToString();
}
///
/// Write all bytes of the input stream to the file given by dstFilePath. dstFilePath is
/// overwritten.
///
public static void WriteAllBytes(Stream srcStream, string dstFilePath)
{
const int MAX_BUFFER_SIZE = 16384;
bool didCreateDstFile = false;
try
{
using (FileStream dstStream = File.OpenWrite(dstFilePath))
{
didCreateDstFile = true;
long srcLength = srcStream.Length;
if (srcLength > 0)
{
long saveSrcPos = srcStream.Position;
srcStream.Position = 0;
try
{
byte[] buffer = new byte[Math.Min(srcLength, MAX_BUFFER_SIZE)];
long bytesLeft = srcLength;
do
{
int processBytes = (int)Math.Min(bytesLeft, buffer.Length);
srcStream.Read(buffer, 0, processBytes);
dstStream.Write(buffer, 0, processBytes);
bytesLeft -= processBytes;
} while (bytesLeft > 0);
}
finally
{
srcStream.Position = saveSrcPos;
}
}
}
}
catch (Exception)
{
if (didCreateDstFile)
{
FileUtility.SafeDeleteFile(dstFilePath);
}
throw;
}
}
///
/// Attempt to create a file without throwing an exception. Return true if the file was created or
/// already existed.
///
public static bool SafeCreateFile(string filePath)
{
try
{
if (File.Exists(filePath))
{
return true;
}
using (File.OpenWrite(filePath)) { }
}
catch (Exception)
{
}
return File.Exists(filePath);
}
///
/// Attempt to delete a file without throwing an exception. Return true if the file was deleted or
/// does not exist.
///
public static bool SafeDeleteFile(string filePath)
{
try
{
if (File.Exists(filePath))
{
File.Delete(filePath);
}
}
catch (Exception)
{
}
return !File.Exists(filePath);
}
///
/// Attempt to delete a directory (and all contents) without throwing an exception. Return true
/// if the directory was deleted or does not exist.
///
public static bool SafeDeleteDirectory(string directoryPath)
{
try
{
Directory.Delete(directoryPath, true);
}
catch (Exception)
{
}
return !Directory.Exists(directoryPath);
}
///
/// Return true if the input directory is empty.
///
public static bool IsDirectoryEmpty(string directoryPath)
{
bool isEmpty = true;
using (FileSystemEnumerator enumerator = new FileSystemEnumerator(directoryPath, "*", false,
FileSystemEnumerator.EReturnTypes.eReturnAll))
{
foreach (string dirPath in enumerator)
{
isEmpty = false;
break;
}
}
return isEmpty;
}
public static void DeleteAllFilesInFolder(string directoryPath)
{
using (FileSystemEnumerator enumerator = new FileSystemEnumerator(directoryPath, "*", false,
FileSystemEnumerator.EReturnTypes.eReturnFiles))
{
foreach (string filePath in enumerator)
{
File.Delete(filePath);
}
}
}
///
/// Delete all files within directoryPath that are older than deleteFilesOlderThanDate. Return the
/// number of files deleted.
///
public static int DeleteAllFilesOlderThan(string directoryPath, DateTime deleteFilesOlderThanDate,
bool includeSubdirectories, bool throwAccessExceptions)
{
int deleteFileCount = 0;
using (FileSystemEnumerator enumerator = new FileSystemEnumerator(directoryPath, "*", includeSubdirectories,
FileSystemEnumerator.EReturnTypes.eReturnFiles))
{
foreach (string filePath in enumerator)
{
try
{
DateTime lastWriteTime = File.GetLastWriteTime(filePath);
if (lastWriteTime < deleteFilesOlderThanDate)
{
File.Delete(filePath);
++deleteFileCount;
}
}
catch (Exception)
{
if (throwAccessExceptions)
{
throw;
}
}
}
}
return deleteFileCount;
}
///
/// Delete all empty folders within directoryPath that are older than deleteFoldersOlderThanDate. Return the
/// number of folders deleted.
///
public static int DeleteAllEmptyFoldersOlderThan(string directoryPath, DateTime deleteFoldersOlderThanDate,
bool throwAccessExceptions)
{
int deleteFolderCount = 0;
using (FileSystemEnumerator enumerator = new FileSystemEnumerator(directoryPath, "*", true,
FileSystemEnumerator.EReturnTypes.eReturnDirectories))
{
foreach (string dirPath in enumerator)
{
try
{
DateTime lastWriteTime = Directory.GetLastWriteTime(dirPath);
if (lastWriteTime < deleteFoldersOlderThanDate)
{
if (FileUtility.IsDirectoryEmpty(dirPath))
{
Directory.Delete(dirPath, true);
++deleteFolderCount;
}
}
}
catch (Exception)
{
if (throwAccessExceptions)
{
throw;
}
}
}
}
return deleteFolderCount;
}
///
/// Return true if the directory directoryPath is writeable.
///
public static bool IsWritableDirectory(string directoryPath)
{
string testFilePath = Path.Combine(directoryPath, Guid.NewGuid().ToString());
try
{
using (FileStream stream = new FileStream(testFilePath, FileMode.CreateNew,
FileAccess.Write, FileShare.None))
{
}
return true;
}
catch (Exception)
{
return false;
}
finally
{
SafeDeleteFile(testFilePath);
}
}
public static void ValidateWritableDirectory(string directoryPath)
{
if (!IsWritableDirectory(directoryPath))
{
throw new UnauthorizedAccessException(string.Format("The application does not have write-access to the folder \"{0}\"",
directoryPath));
}
}
public static void ValidateFileExists(string filePath)
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException(string.Format("The application could not locate the file \"{0}\"",
filePath));
}
}
public static void ValidateFolderExists(string folderPath)
{
if (!Directory.Exists(folderPath))
{
throw new DirectoryNotFoundException(string.Format("The application could not locate the folder \"{0}\"",
folderPath));
}
}
public static void SetReadOnly(string filePath, bool isReadOnly)
{
FileAttributes attributes = File.GetAttributes(filePath);
if (isReadOnly)
{
if (EnumUtility.IsFlagSet(attributes, FileAttributes.ReadOnly))
{
return;
}
attributes = EnumUtility.SetFlag(attributes, FileAttributes.ReadOnly);
}
else
{
if (!EnumUtility.IsFlagSet(attributes, FileAttributes.ReadOnly))
{
return;
}
attributes = EnumUtility.ClearFlag(attributes, FileAttributes.ReadOnly);
}
File.SetAttributes(filePath, attributes);
}
///
/// Make a unique file name within the directory directoryPath using fileName as a base name for
/// the file.
///
public static string MakeUniqueIncrementalFilePath(string directoryPath, string fileName)
{
string path = Path.Combine(directoryPath, fileName);
if (!File.Exists(path))
{
return path;
}
string fileNameMinusExtension = Path.GetFileNameWithoutExtension(fileName);
string extension = Path.GetExtension(fileName);
for (int i = 1; ; ++i)
{
path = Path.Combine(directoryPath, Path.ChangeExtension(fileNameMinusExtension + '_' + i.ToString(),
extension));
if (!File.Exists(path))
{
return path;
}
}
}
///
/// Make a unique file name within the directory directoryPath using fileName as a base name for
/// the file.
///
public static string GetOldestFile(string directoryPath, string fileTypesToMatch, bool includeSubDirs)
{
string filePath = null;
DateTime fileTime = DateTime.MaxValue;
using (FileSystemEnumerator enumerator = new FileSystemEnumerator(directoryPath, fileTypesToMatch, includeSubDirs,
FileSystemEnumerator.EReturnTypes.eReturnFiles))
{
foreach (string curFilePath in enumerator)
{
DateTime curWriteTime = File.GetLastWriteTime(curFilePath);
if ((filePath == null) || (fileTime > curWriteTime))
{
filePath = curFilePath;
fileTime = curWriteTime;
}
}
}
return filePath;
}
public static void GrantEveryoneFullAccessToFile(string filePath)
{
FileSecurity security = File.GetAccessControl(filePath);
security.AddAccessRule(new FileSystemAccessRule("Everyone", FileSystemRights.FullControl, AccessControlType.Allow));
File.SetAccessControl(filePath, security);
}
///
/// Return the full path to a file relative to the currently executing assembly.
///
public static string GetExecutingAssemblyRelativePath(string fileName)
{
return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), fileName);
}
///
/// Return a friendly, Windows-Explorer-formatted string representing the size of a file.
///
public static string GetDisplayFileSize(long fileSize)
{
StringBuilder sbBuffer = new StringBuilder(32);
StrFormatByteSize(fileSize, sbBuffer, sbBuffer.Capacity);
return sbBuffer.ToString();
}
[DllImport("Shlwapi.dll", CharSet = CharSet.Auto)]
private static extern long StrFormatByteSize(long fileSize, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder buffer,
int bufferSize);
}
///
/// Same as StreamWriter, but doesn't close the underlying stream when the writer closes
///
public class NoCloseStreamWriter : StreamWriter
{
public NoCloseStreamWriter(Stream stream) : base(stream) { }
public NoCloseStreamWriter(string path) : base(path) { }
public NoCloseStreamWriter(Stream stream, Encoding encoding) : base(stream, encoding) { }
public NoCloseStreamWriter(string path, bool append) : base(path, append) { }
public NoCloseStreamWriter(Stream stream, Encoding encoding, int bufferSize) : base(stream, encoding, bufferSize) { }
public NoCloseStreamWriter(string path, bool append, Encoding encoding) : base(path, append, encoding) { }
public NoCloseStreamWriter(string path, bool append, Encoding encoding, int bufferSize) : base(path, append, encoding, bufferSize) { }
protected override void Dispose(bool disposing)
{
ReflectionUtility.SetFieldValue(this, "stream", null);
base.Dispose(disposing);
}
}
///
/// Same as StreamReader, but doesn't close the underlying stream when the reader closes
///
public class NoCloseStreamReader : StreamReader
{
public NoCloseStreamReader(Stream stream) : base(stream) { }
public NoCloseStreamReader(string path) : base(path) { }
public NoCloseStreamReader(Stream stream, bool detectEncodingFromByteOrderMarks) : base(stream, detectEncodingFromByteOrderMarks) { }
public NoCloseStreamReader(Stream stream, Encoding encoding) : base(stream, encoding) { }
public NoCloseStreamReader(string path, bool detectEncodingFromByteOrderMarks) : base(path, detectEncodingFromByteOrderMarks) { }
public NoCloseStreamReader(string path, Encoding encoding) : base(path, encoding) { }
public NoCloseStreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks) : base(stream, encoding, detectEncodingFromByteOrderMarks) { }
public NoCloseStreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks) : base(path, encoding, detectEncodingFromByteOrderMarks) { }
public NoCloseStreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize) : base(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize) { }
public NoCloseStreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize) : base(path, encoding, detectEncodingFromByteOrderMarks, bufferSize) { }
protected override void Dispose(bool disposing)
{
ReflectionUtility.SetFieldValue(this, "stream", null);
base.Dispose(disposing);
}
}
public class FileSaveOverwriter : DisposableBase
{
private string m_FileToOverwrite;
private string m_TempFilePath;
private bool m_Successful;
public FileSaveOverwriter(string fileToOverwrite, string tempFileExtension)
{
m_FileToOverwrite = fileToOverwrite;
m_TempFilePath = Path.Combine(Path.GetDirectoryName(fileToOverwrite), Guid.NewGuid().ToString() + tempFileExtension);
}
public FileSaveOverwriter(string fileToOverwrite) : this(fileToOverwrite, ".tmp")
{
}
public string TempFilePath
{
get { return m_TempFilePath; }
set { m_TempFilePath = value; }
}
public bool Successful
{
get { return m_Successful; }
set { m_Successful = value; }
}
public string FileToOverwrite
{
get { return m_FileToOverwrite; }
set { m_FileToOverwrite = value; }
}
protected override void OnDispose(bool inIsDisposing)
{
if (inIsDisposing)
{
if (m_Successful)
{
if (File.Exists(m_FileToOverwrite))
{
File.Delete(m_FileToOverwrite);
}
File.Move(m_TempFilePath, m_FileToOverwrite);
}
else
{
FileUtility.SafeDeleteFile(m_TempFilePath);
}
}
}
}
public class TempFileDeleter : DisposableBase
{
private List m_FilesToDelete;
public void Add(string filePath)
{
if (m_FilesToDelete == null)
{
m_FilesToDelete = new List();
}
if (!m_FilesToDelete.Contains(filePath))
{
m_FilesToDelete.Add(filePath);
}
}
public void Remove(string filePath)
{
if (m_FilesToDelete != null)
{
m_FilesToDelete.Remove(filePath);
}
}
protected override void OnDispose(bool inIsDisposing)
{
if (inIsDisposing)
{
if (!CollectionUtility.IsNullOrEmpty(m_FilesToDelete))
{
foreach (string filePath in m_FilesToDelete)
{
FileUtility.SafeDeleteFile(filePath);
}
}
}
}
}
}