Skip to content

Instantly share code, notes, and snippets.

@osmyn
Last active January 4, 2019 21:12
Show Gist options
  • Save osmyn/78f5f1f4f13ee3b0cfafc31bff0ce173 to your computer and use it in GitHub Desktop.
Save osmyn/78f5f1f4f13ee3b0cfafc31bff0ce173 to your computer and use it in GitHub Desktop.
File utilities
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;
}
}
}
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);
}
}
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();
}
}
}
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