// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Data; using System.IO; using System.Security.Cryptography; using System.Runtime.InteropServices; namespace Spludlow.Io { public class Files { private const int BufferSize = 8 * 1024 * 1024; public static long FileLength(string filename) { if (File.Exists(filename) == false) return -1; FileInfo info = new FileInfo(filename); return info.Length; } public static byte[] ReadAllBytes(Stream stream) { using (MemoryStream memoryStream = new MemoryStream()) { stream.CopyTo(memoryStream); return memoryStream.ToArray(); } } public static string ReadAllText(Stream stream) { return ReadAllText(stream, Encoding.Default); } public static string ReadAllText(Stream stream, Encoding encoding) { using (StreamReader reader = new StreamReader(stream, encoding)) return reader.ReadToEnd(); } public static string HashMD5(string filename) { using (MD5 hash = MD5.Create()) using (FileStream stream = File.OpenRead(filename)) return HashText(hash.ComputeHash(stream)); } public static string HashMD5(byte[] data) { using (MD5 hash = MD5.Create()) return HashText(hash.ComputeHash(data)); } public static string HashSHA1(string filename) { using (SHA1 hash = SHA1.Create()) using (FileStream stream = File.OpenRead(filename)) return HashText(hash.ComputeHash(stream)); } public static string HashSHA1(byte[] data) { using (SHA1 hash = SHA1.Create()) return HashText(hash.ComputeHash(data)); } public static string HashSHA256(string filename) { using (SHA256 hash = SHA256.Create()) using (FileStream stream = File.OpenRead(filename)) return HashText(hash.ComputeHash(stream)); } public static string HashSHA256(byte[] data) { using (SHA256 hash = SHA256.Create()) return HashText(hash.ComputeHash(data)); } private static string HashText(byte[] hash) { return BitConverter.ToString(hash).Replace("-", "").ToLower(); } public static string UniqueExistingName(string filename) { if (File.Exists(filename) == false) return filename; string directory = Path.GetDirectoryName(filename); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filename); string extension = Path.GetExtension(filename); int existingCount = 2; while (File.Exists(filename) == true) { filename = directory + @"\" + fileNameWithoutExtension + " (" + existingCount + ")" + extension; ++existingCount; } return filename; } public static string[][] ReadTabWords(string filename) { List lines = new List(); foreach (string rawLine in File.ReadAllLines(filename)) { string line = rawLine.Trim(); if (line.Length == 0 || line.StartsWith("#") == true) continue; string[] words = Spludlow.Text.Split(line, '\t', true); lines.Add(words); } return lines.ToArray(); } public static void DeletePersistently(string fileName) { int attempts = 4; int wait = 3000; for (int attempt = 0; attempt < attempts; ++attempt) { if (attempt > 0) System.Threading.Thread.Sleep(wait); try { if (File.Exists(fileName) == false) break; File.Delete(fileName); } catch (System.IO.IOException ee) { Spludlow.Log.Warning("Delete File Persistently, IOException: " + ee.Message, fileName); } catch (System.UnauthorizedAccessException ee) { Spludlow.Log.Warning("Delete File Persistently, UnauthorizedAccessException: " + ee.Message, fileName); } } if (File.Exists(fileName) == true) throw new ApplicationException("Delete File Persistently; Can not delete file: " + fileName); } public static bool IsOpen(string fileName) { FileStream stream = null; try { stream = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.None); return false; } catch (IOException ee) { if ((uint)ee.HResult == 0x80070020) return true; throw ee; } finally { if (stream != null) stream.Close(); } } public static void SafeAppendAllText(string path, string contents) { SafeAppendAllText(path, contents, Encoding.Default); } public static void SafeAppendAllText(string path, string contents, Encoding encoding) { using (FileStream fileStream = FileStreamAppendWrite(path)) { using (StreamWriter writer = new StreamWriter(fileStream, encoding)) { writer.Write(contents); } } } public static void SafeWriteAllText(string path, string contents) { SafeWriteAllText(path, contents, Encoding.Default); } public static void SafeWriteAllText(string path, string contents, Encoding encoding) { using (FileStream fileStream = FileStreamCreateWrite(path)) { using (StreamWriter writer = new StreamWriter(fileStream, encoding)) { writer.Write(contents); } } } public static void SafeWriteAllLines(string path, string[] contents) { SafeWriteAllLines(path, contents, Encoding.Default); } public static void SafeWriteAllLines(string path, string[] contents, Encoding encoding) { using (FileStream fileStream = FileStreamCreateWrite(path)) { using (StreamWriter writer = new StreamWriter(fileStream, encoding)) { foreach (string line in contents) writer.WriteLine(line); } } } public static string[] SafeReadAllLines(string path) { return SafeReadAllLines(path, Encoding.Default); } public static string[] SafeReadAllLines(string path, Encoding encoding) { List lines = new List(); using (FileStream fileStream = FileStreamOpenRead(path)) { using (StreamReader reader = new StreamReader(fileStream, encoding)) { string line; while ((line = reader.ReadLine()) != null) { lines.Add(line); } } } return lines.ToArray(); } public static string SafeReadAllText(string path) { return SafeReadAllText(path, Encoding.Default); } public static string SafeReadAllText(string path, Encoding encoding) { using (FileStream fileStream = FileStreamOpenRead(path)) { using (StreamReader reader = new StreamReader(fileStream, encoding)) return reader.ReadToEnd(); } } public static FileStream FileStreamAppendReadWrite(string path) { return FileStreamSafe(path, FileMode.Append, FileAccess.ReadWrite, FileShare.None); } public static FileStream FileStreamOpenOrCreateReadWrite(string path) { return FileStreamSafe(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); } public static FileStream FileStreamAppendWrite(string path) { return FileStreamSafe(path, FileMode.Append, FileAccess.Write, FileShare.None); } public static FileStream FileStreamOpenReadWrite(string path) { return FileStreamSafe(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None); } public static FileStream FileStreamOpenRead(string path) { return FileStreamSafe(path, FileMode.Open, FileAccess.Read, FileShare.None); } public static FileStream FileStreamCreateWrite(string path) { return FileStreamSafe(path, FileMode.Create, FileAccess.Write, FileShare.None); } public static FileStream FileStreamSafe(string path, FileMode mode, FileAccess access, FileShare share) { int attempts = 8; int wait = 250; for (int attempt = 0; attempt < attempts; ++attempt) { if (attempt > 0) System.Threading.Thread.Sleep(wait); wait += 250; // Take a bit longer each time try { return new FileStream(path, mode, access, share); } catch (IOException ee) { if ((uint)ee.HResult != 0x80070020) // The process cannot access the file '' because it is being used by another process. throw ee; Spludlow.Log.Warning("File Stream Safe Locked; Attempt: " + attempt + ", Filename: " + path); } } throw new ApplicationException("File Stream Safe Locked Failed All Attempts: " + path); } [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] static extern bool MoveFile(string lpExistingFileName, string lpNewFileName); public static void FixLongFilenames(string rootDirectory) { DataTable table = Spludlow.Data.TextTable.ReadText(new string[] { "Filename FilenameLength Directory Extention StartName StartLength EndName EndLength", "String Int32 String String String Int32 String Int32", }); int max = 260; foreach (string subDirectory in Spludlow.Io.Dirs.GetDirectories(rootDirectory)) { foreach (string fullFilename in Directory.GetFiles(subDirectory)) { if (fullFilename.Length >= max) { int reduce = fullFilename.Length - (max - 1); int index = fullFilename.LastIndexOf(@"\"); string directory = fullFilename.Substring(0, index); if (directory.Length >= 248) throw new ApplicationException("Impossibly Large (>= 248) Directory Name:\t" + directory + ", " + directory.Length); string originalFilename = fullFilename.Substring(index + 1); string filename = originalFilename; string extention = null; index = filename.LastIndexOf("."); if (index != -1) { extention = filename.Substring(index); filename = filename.Substring(0, index); } filename = filename.Substring(0, filename.Length - reduce); StringBuilder result = new StringBuilder(); result.Append(directory); result.Append(@"\"); result.Append(filename.Trim()); // may end up lower if (extention != null) result.Append(extention); string newName = result.ToString(); MoveFile(@"\\?\" + fullFilename, newName); // Old API Works FileInfo info = new FileInfo(newName); // Check OK table.Rows.Add(new object[] { filename, filename.Length, directory, extention, fullFilename, fullFilename.Length, newName, newName.Length }); } } } Spludlow.Log.Report("FixLongFilenames", new object[] { table }); } public static string SplitFile(string sourceFilename, string destinationDirectory, long chunkSize) { byte[] buffer = new byte[chunkSize]; string name = Path.GetFileName(sourceFilename); List chunkFilesnames = new List(); using (FileStream readStream = new FileStream(sourceFilename, FileMode.Open)) { int read; for (int partIndex = 0; (read = readStream.Read(buffer, 0, buffer.Length)) > 0; ++partIndex) { string chunkFilename = destinationDirectory + @"\" + name + "." + partIndex.ToString("000"); if (File.Exists(chunkFilename) == true) { File.Delete(chunkFilename); } using (FileStream writeStream = new FileStream(chunkFilename, FileMode.Create)) { writeStream.Write(buffer, 0, read); } chunkFilesnames.Add(chunkFilename); } } // copy /b file1 + file2 file1 StringBuilder command = new StringBuilder(); command.Append("copy /b "); for (int index = 0; index < chunkFilesnames.Count; ++index) { string chunkFilesname = Path.GetFileName(chunkFilesnames[index]); if (index > 0) command.Append(" + "); command.Append(chunkFilesname); } command.Append(" "); command.Append(Path.GetFileName(sourceFilename)); string text = command.ToString(); Spludlow.Log.Report("Split Command", text); return text; } public static void TestSafeReadWrite(string filename, int threads, int iterations) { Spludlow.Log.Report("TestSafeReadWrite Starting"); if (File.Exists(filename) == true) File.Delete(filename); File.WriteAllText(filename, DateTime.Now.ToString() + Environment.NewLine); Task[] tasks = new Task[threads]; for (int index = 0; index < threads; ++index) { int threadId = index; tasks[index] = new Task(() => TestSafeReadWriteWorker(filename, iterations, threadId)); } foreach (Task task in tasks) task.Start(); Task.WaitAll(tasks); } private static void TestSafeReadWriteWorker(string filename, int iterations, int threadId) { Random rand = new Random(); for (int pass = 0; pass < iterations; ++pass) { string data; using (FileStream fileStream = FileStreamOpenReadWrite(filename)) { using (StreamReader reader = new StreamReader(fileStream, Encoding.ASCII)) { data = reader.ReadToEnd(); fileStream.Position = 0; data += DateTime.Now.ToString() + "\t" + threadId + "\t" + pass + Environment.NewLine; using (StreamWriter writer = new StreamWriter(fileStream, Encoding.ASCII)) { writer.Write(data); } System.Threading.Thread.Sleep(rand.Next(500)); } } Spludlow.Log.Info("TestSafeReadWrit: Thread:" + threadId + ", Pass:" + pass); } } } }