Last active
January 4, 2019 21:12
-
-
Save osmyn/78f5f1f4f13ee3b0cfafc31bff0ce173 to your computer and use it in GitHub Desktop.
File utilities
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using BusinessLogic.Utilities.Interfaces; | |
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.IO.Compression; | |
using System.Linq; | |
using System.Text; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using BusinessLogic.Logging; | |
namespace BusinessLogic.Utilities | |
{ | |
public class FileHelper : IFileHelper | |
{ | |
/// <summary> | |
/// Remove illegal characters from a file name | |
/// </summary> | |
/// <param name="fileName"></param> | |
/// <returns></returns> | |
public string CleanedFileName(string fileName) | |
{ | |
return Path.GetInvalidFileNameChars() | |
.Aggregate(fileName, (current, c) => current.Replace(c.ToString(), string.Empty)); | |
} | |
/// <summary> | |
/// Remove illegal characters from a path | |
/// </summary> | |
/// <param name="path"></param> | |
/// <returns></returns> | |
public string CleanedPath(string path) | |
{ | |
return Path.GetInvalidPathChars() | |
.Aggregate(path, (current, c) => current.Replace(c.ToString(), string.Empty)); | |
} | |
/// <summary> | |
/// Cleans a path and file name | |
/// </summary> | |
/// <param name="fullPath"></param> | |
/// <returns></returns> | |
public string CleanedFullPath(string fullPath) | |
{ | |
var file = CleanedFileName(Path.GetFileName(fullPath)); | |
var path = CleanedPath(Path.GetDirectoryName(fullPath)); | |
return Path.Combine(path, file); | |
} | |
/// <summary> | |
/// Check if a directory exits | |
/// </summary> | |
/// <param name="dirPath"></param> | |
/// <exception cref="System.IO.IOException">Throws an IO exception</exception> | |
public void ValidateDirectoryExists(string dirPath) | |
{ | |
dirPath = CleanedPath(Path.GetDirectoryName(dirPath)); | |
if (!Directory.Exists(dirPath)) | |
{ | |
throw new IOException($"Directory '{dirPath}' did not exist"); | |
} | |
} | |
/// <summary> | |
/// Validates a file exists | |
/// </summary> | |
/// <param name="fullPath"></param> | |
public void ValidateFileExists(string fullPath) | |
{ | |
fullPath = CleanedFullPath(fullPath); | |
if (!File.Exists(fullPath)) | |
{ | |
throw new IOException("File not found"); | |
} | |
} | |
/// <summary> | |
/// Creates all the directories in the provided | |
/// fullPath if they do not exist | |
/// </summary> | |
/// <param name="fullPath"></param> | |
public void CreateDirectories(string fullPath) | |
{ | |
fullPath = CleanedFullPath(fullPath); | |
try | |
{ | |
var fileInfo = new FileInfo(fullPath); | |
fileInfo.Directory.Create(); | |
} | |
catch (IOException ex) | |
{ | |
throw new IOException($"Directory '{fullPath}' could not be created"); | |
} | |
} | |
/// <summary> | |
/// Read the file | |
/// </summary> | |
/// <param name="fullPath"></param> | |
/// <param name="encoding"></param> | |
/// <param name="maxSecondsWaitForUnlock"></param> | |
/// <returns></returns> | |
public string[] ReadContents(string fullPath, Encoding encoding, int maxSecondsWaitForUnlock) | |
{ | |
fullPath = CleanedFullPath(fullPath); | |
ValidateFileExists(fullPath); | |
if (maxSecondsWaitForUnlock > 0) | |
WaitForFileToUnlock(new FileInfo(fullPath), maxSecondsWaitForUnlock); | |
return File.ReadAllLines(fullPath, encoding); | |
} | |
private void WaitForFileToUnlock(FileInfo file, int maxSeconds) | |
{ | |
var iSleepCount = 0; | |
while (IsFileLocked(file)) | |
{ | |
Thread.Sleep(100); | |
iSleepCount++; | |
if (iSleepCount >= maxSeconds * 10) | |
{ | |
throw new IOException(string.Format("File '{0}' was locked for more than '{1}' seconds.", | |
file.FullName, maxSeconds)); | |
} | |
} | |
} | |
private bool IsFileLocked(FileInfo file) | |
{ | |
FileStream stream = null; | |
try | |
{ | |
stream = file.Open(FileMode.Open, | |
FileAccess.Read, FileShare.Read); | |
} | |
catch (IOException) | |
{ | |
//the file is unavailable because it is: | |
//still being written to | |
//or being processed by another thread | |
return true; | |
} | |
finally | |
{ | |
if (stream != null) | |
stream.Close(); | |
} | |
//file is not locked | |
return false; | |
} | |
/// <summary> | |
/// Wraps File.WriteAllText | |
/// </summary> | |
/// <param name="fullPath"></param> | |
/// <param name="contents"></param> | |
public void WriteAllText(string fullPath, string contents) | |
{ | |
if (string.IsNullOrEmpty(contents)) return; | |
fullPath = CleanedFullPath(fullPath); | |
ValidateDirectoryExists(fullPath); | |
File.WriteAllText(fullPath, contents); | |
} | |
/// <summary> | |
/// Return a list of files starting with a certain string | |
/// </summary> | |
/// <param name="path"></param> | |
/// <param name="startsWith"></param> | |
/// <returns></returns> | |
public string[] GetFilesStartingWith(string path, string startsWith) | |
{ | |
if (string.IsNullOrWhiteSpace(startsWith)) return null; | |
startsWith = CleanedFileName(startsWith); | |
path = CleanedPath(path); | |
return new DirectoryInfo(path) | |
.GetFiles(startsWith + "*.*") | |
.Select(f => f.FullName) | |
.OrderBy(s => s) | |
.ToArray(); | |
} | |
/// <summary> | |
/// Clean up a saga's generated files with this utility | |
/// </summary> | |
/// <param name="path"></param> | |
/// <param name="startsWith"></param> | |
/// <param name="logger"></param> | |
public void DeleteFilesStartingWith(string path, string startsWith, ILogger logger) | |
{ | |
if (string.IsNullOrWhiteSpace(startsWith)) return; | |
startsWith = CleanedFileName(startsWith); | |
path = CleanedPath(path); | |
var files = new DirectoryInfo(path) | |
.GetFiles(startsWith + "*.*"); | |
var loggedWarning = false; | |
foreach (var file in files) | |
{ | |
try | |
{ | |
file.Delete(); | |
} | |
catch (Exception ex) | |
{ | |
if (!loggedWarning) | |
{ | |
logger.Warn($"Failed to delete some generated files starting with '{startsWith}' at '{path}'", ex); | |
loggedWarning = true; | |
} | |
} | |
} | |
} | |
/// <summary> | |
/// Copy a file from one spot to another | |
/// </summary> | |
/// <param name="sourceFullPath"></param> | |
/// <param name="destinationFullPath"></param> | |
public void CopyFile(string sourceFullPath, string destinationFullPath) | |
{ | |
File.Copy(CleanedFullPath(sourceFullPath), CleanedFullPath(destinationFullPath), overwrite:true); | |
} | |
/// <summary> | |
/// Replaces an input file with a zipped file | |
/// </summary> | |
/// <param name="sourceFullPath"></param> | |
/// <returns>Full path to zip file</returns> | |
public string ReplaceWithZipFile(string sourceFullPath) | |
{ | |
sourceFullPath = CleanedFullPath(sourceFullPath); | |
var zipFullPath = CleanedFullPath(sourceFullPath + ".zip"); | |
//clean up old file | |
if (File.Exists(zipFullPath)) | |
File.Delete(zipFullPath); | |
using (ZipArchive zip = ZipFile.Open(zipFullPath, ZipArchiveMode.Create)) | |
{ | |
zip.CreateEntryFromFile(sourceFullPath, Path.GetFileName(sourceFullPath)); | |
} | |
//clean up original file | |
File.Delete(sourceFullPath); | |
return zipFullPath; | |
} | |
/// <summary> | |
/// Replaces any illegal characters in a csv value | |
/// </summary> | |
public string EscapeCsv(string val) | |
{ | |
if (string.IsNullOrWhiteSpace(val)) return val; | |
//escape CSV-Injection | |
var escapeChars = new[] { '=', '+', '-', '@' }; | |
val = val.TrimStart(escapeChars); | |
//escape quotes within a value | |
if (val.IndexOf("\"") >= 0) | |
val = val.Replace("\"", "\"\""); | |
//quote entire value if comma exists within | |
if (val.IndexOf(",") >= 0) | |
val = "\"" + val + "\""; | |
//escape new lines | |
while (val.Contains("\r")) | |
{ | |
val = val.Replace("\r", ""); | |
} | |
while (val.Contains("\n")) | |
{ | |
val = val.Replace("\n", ""); | |
} | |
return val; | |
} | |
public MemoryStream CreateZipStream(MemoryStream memoryStream, string internalFileName, MemoryStream contentStream) | |
{ | |
using (var zip = new ZipArchive(memoryStream, ZipArchiveMode.Create, leaveOpen: true)) | |
{ | |
var entry = zip.CreateEntry(internalFileName); | |
using (var entryStream = entry.Open()) | |
{ | |
(await contentStream.CopyTo(entryStream); | |
} | |
} | |
memoryStream.Position = 0; | |
return meomoryStream; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Text; | |
using BusinessLogic.Logging; | |
namespace BusinessLogic.Utilities.Interfaces | |
{ | |
public interface IFileHelper | |
{ | |
/// <summary> | |
/// Remove illegal characters from a file name | |
/// </summary> | |
/// <param name="fileName"></param> | |
/// <returns></returns> | |
string CleanedFileName(string fileName); | |
/// <summary> | |
/// Remove illegal characters from a path | |
/// </summary> | |
/// <param name="path"></param> | |
/// <returns></returns> | |
string CleanedPath(string path); | |
/// <summary> | |
/// Cleans a path and file name | |
/// </summary> | |
/// <param name="fullPath"></param> | |
/// <returns></returns> | |
string CleanedFullPath(string fullPath); | |
/// <summary> | |
/// Check if a directory exits | |
/// </summary> | |
/// <param name="dirPath"></param> | |
/// <exception cref="System.IO.IOException">Throws an IO exception</exception> | |
void ValidateDirectoryExists(string dirPath); | |
/// <summary> | |
/// Validates a file exists | |
/// </summary> | |
/// <param name="fullPath"></param> | |
void ValidateFileExists(string fullPath); | |
/// <summary> | |
/// Creates all the directories in the provided | |
/// fullPath if they do not exist | |
/// </summary> | |
/// <param name="fullPath"></param> | |
void CreateDirectories(string fullPath); | |
/// <summary> | |
/// Read the file | |
/// </summary> | |
/// <param name="fullPath"></param> | |
/// <param name="encoding"></param> | |
/// <param name="maxSecondsWaitForUnlock"></param> | |
/// <returns></returns> | |
string[] ReadContents(string fullPath, Encoding encoding, int maxSecondsWaitForUnlock); | |
/// <summary> | |
/// Wraps File.WriteAllText | |
/// </summary> | |
/// <param name="fullPath"></param> | |
/// <param name="contents"></param> | |
void WriteAllText(string fullPath, string contents); | |
/// <summary> | |
/// Return a list of files starting with a certain string | |
/// </summary> | |
/// <param name="path"></param> | |
/// <param name="startsWith"></param> | |
/// <returns></returns> | |
string[] GetFilesStartingWith(string path, string startsWith); | |
/// <summary> | |
/// Clean up a saga's generated files with this utility | |
/// </summary> | |
/// <param name="path"></param> | |
/// <param name="startsWith"></param> | |
/// <param name="logger"></param> | |
void DeleteFilesStartingWith(string path, string startsWith, ILogger logger); | |
/// <summary> | |
/// Copy a file from one spot to another | |
/// </summary> | |
/// <param name="sourceFullPath"></param> | |
/// <param name="destinationFullPath"></param> | |
void CopyFile(string sourceFullPath, string destinationFullPath); | |
/// <summary> | |
/// Replaces an input file with a zipped file | |
/// </summary> | |
/// <param name="sourceFullPath"></param> | |
/// <returns>Full path to zip file</returns> | |
string ReplaceWithZipFile(string sourceFullPath); | |
/// <summary> | |
/// Replaces any illegal characters in a csv value | |
/// </summary> | |
string EscapeCsv(string val); | |
/// <summary> | |
/// Creates a zip file stream | |
/// </summary> | |
/// <example> | |
/// var ms = new MemoryStream(); | |
/// var zipStream = CreateZipStream(ms, "myFile.csv", content); | |
/// return File(ms, "application/octet-stream", "myFile.zip"); | |
/// </example> | |
MemoryStream CreateZipStream(MemoryStream memoryStream, string internalFileName, MemoryStream contentStream); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Text; | |
using System.Threading.Tasks; | |
namespace BusinessLogic.Utilities | |
{ | |
public interface IWrapStreamReader : IDisposable | |
{ | |
string ReadLine(); | |
void Close(); | |
void DeleteFile(); | |
} | |
public class WrapStreamReader : IWrapStreamReader | |
{ | |
private StreamReader _reader; | |
private string _fullPath = ""; | |
public WrapStreamReader(string fullPath) | |
{ | |
_fullPath = fullPath; | |
_reader = new StreamReader(fullPath); | |
} | |
public void Close() | |
{ | |
_reader.Close(); | |
} | |
public void Dispose() | |
{ | |
_reader.Dispose(); | |
} | |
public void DeleteFile() | |
{ | |
if (File.Exists(_fullPath)) | |
{ | |
try | |
{ | |
Close(); | |
Dispose(); | |
File.Delete(_fullPath); | |
} | |
catch | |
{ | |
// ignored | |
} | |
} | |
} | |
public string ReadLine() | |
{ | |
return _reader.ReadLine(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
namespace BusinessLogic.Utilities | |
{ | |
public interface IWrapStreamWriter : IDisposable | |
{ | |
void WriteLine(string value); | |
IWrapStreamWriter CreateText(string fullPath); | |
} | |
public class WrapStreamWriter : IWrapStreamWriter | |
{ | |
private StreamWriter _writer; | |
public IWrapStreamWriter CreateText(string fullPath) | |
{ | |
_writer = new StreamWriter(fullPath); | |
return this; | |
} | |
public void Dispose() | |
{ | |
_writer.Dispose(); | |
} | |
public void WriteLine(string value) | |
{ | |
if (_writer == null) | |
throw new ApplicationException("Must call CreateText first."); | |
_writer.WriteLine(value); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment