You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							791 lines
						
					
					
						
							30 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							791 lines
						
					
					
						
							30 KiB
						
					
					
				
								using Apewer.Internals;
							 | 
						|
								using System;
							 | 
						|
								using System.Collections.Generic;
							 | 
						|
								using System.IO;
							 | 
						|
								using System.Reflection;
							 | 
						|
								using System.Security.Permissions;
							 | 
						|
								
							 | 
						|
								namespace Apewer
							 | 
						|
								{
							 | 
						|
								
							 | 
						|
								    /// <summary>存储实用工具。</summary>
							 | 
						|
								    public static class StorageUtility
							 | 
						|
								    {
							 | 
						|
								
							 | 
						|
								        private static Exception Try(Action action)
							 | 
						|
								        {
							 | 
						|
								            try { action?.Invoke(); return null; }
							 | 
						|
								            catch (Exception ex) { return ex; }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>文件操作线程锁。</summary>
							 | 
						|
								        public static object Locker = new object();
							 | 
						|
								
							 | 
						|
								        #region NTFS 流
							 | 
						|
								
							 | 
						|
								        /// <summary>获取标记的路径。</summary>
							 | 
						|
								        public static string GetZoneIdentifier(string path) => NtfsUnlocker.GetZoneIdentifier(path);
							 | 
						|
								
							 | 
						|
								        /// <summary>检查标记是否存在。</summary>
							 | 
						|
								        public static bool ZoneIdentifierExists(string zoneIdentifier) => NtfsUnlocker.ZoneIdentifierExists(zoneIdentifier);
							 | 
						|
								
							 | 
						|
								        /// <summary>删除标记。</summary>
							 | 
						|
								        public static bool DeleteZoneIdentifier(string zoneIdentifier) => NtfsUnlocker.DeleteZoneIdentifier(zoneIdentifier);
							 | 
						|
								
							 | 
						|
								        #endregion
							 | 
						|
								
							 | 
						|
								        #region delete
							 | 
						|
								
							 | 
						|
								        /// <summary>删除文件。</summary>
							 | 
						|
								        public static Exception DeleteFile(string path, bool useTemp = false)
							 | 
						|
								        {
							 | 
						|
								            if (useTemp)
							 | 
						|
								            {
							 | 
						|
								                try
							 | 
						|
								                {
							 | 
						|
								                    if (string.IsNullOrEmpty(path)) return new ArgumentException();
							 | 
						|
								                    if (!File.Exists(path)) return new FileNotFoundException();
							 | 
						|
								
							 | 
						|
								                    Try(() => new FileInfo(path).Attributes = FileAttributes.Normal);
							 | 
						|
								
							 | 
						|
								                    var temp = Environment.GetEnvironmentVariable("TEMP");
							 | 
						|
								                    var name = Path.GetFileName(path);
							 | 
						|
								
							 | 
						|
								                    var dest = null as string;
							 | 
						|
								                    while (dest == null || File.Exists(dest))
							 | 
						|
								                    {
							 | 
						|
								                        var guid = Guid.NewGuid().ToString("n").Substring(0, 8);
							 | 
						|
								                        dest = $"trash_{guid}_{name}";
							 | 
						|
								                    }
							 | 
						|
								                    File.Move(path, dest);
							 | 
						|
								                    File.Delete(dest);
							 | 
						|
								                    return null;
							 | 
						|
								                }
							 | 
						|
								                catch (Exception ex) { return ex; }
							 | 
						|
								            }
							 | 
						|
								            try
							 | 
						|
								            {
							 | 
						|
								                File.Delete(path);
							 | 
						|
								                return null;
							 | 
						|
								            }
							 | 
						|
								            catch (Exception ex) { return ex; }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>删除目录、子目录和子文件。</summary>
							 | 
						|
								        public static Exception DeleteDirectory(string path, bool useTemp = false)
							 | 
						|
								        {
							 | 
						|
								            if (useTemp)
							 | 
						|
								            {
							 | 
						|
								                try
							 | 
						|
								                {
							 | 
						|
								                    if (string.IsNullOrEmpty(path)) return new ArgumentException();
							 | 
						|
								                    if (!Directory.Exists(path)) return new DirectoryNotFoundException();
							 | 
						|
								
							 | 
						|
								                    var temp = Environment.GetEnvironmentVariable("TEMP");
							 | 
						|
								                    var name = Path.GetDirectoryName(path);
							 | 
						|
								
							 | 
						|
								                    var dest = null as string;
							 | 
						|
								                    while (dest == null || Directory.Exists(dest))
							 | 
						|
								                    {
							 | 
						|
								                        var guid = Guid.NewGuid().ToString("n").Substring(0, 8);
							 | 
						|
								                        dest = $"trash_{guid}_{name}";
							 | 
						|
								                    }
							 | 
						|
								                    Directory.Move(path, dest);
							 | 
						|
								                    Directory.Delete(dest, true);
							 | 
						|
								                    return null;
							 | 
						|
								                }
							 | 
						|
								                catch (Exception ex) { return ex; }
							 | 
						|
								            }
							 | 
						|
								            try
							 | 
						|
								            {
							 | 
						|
								                Directory.Delete(path, true);
							 | 
						|
								                return null;
							 | 
						|
								            }
							 | 
						|
								            catch (Exception ex) { return ex; }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        #endregion
							 | 
						|
								
							 | 
						|
								        #region path
							 | 
						|
								
							 | 
						|
								        /// <summary>无效的路径字符。</summary>
							 | 
						|
								        public static char[] InvalidPathChars
							 | 
						|
								        {
							 | 
						|
								            // SMB 共享文件夹无效字符
							 | 
						|
								            // ! " # % & ' ( ) * + , / : ; < = > ? @ [ ] \ ^ ` { } | ~
							 | 
						|
								            get => new char[] {
							 | 
						|
								                '\\', '/', '\'', '"', ':', '*', '?', '<', '>', '|',
							 | 
						|
								                '\0', '\a', '\b', '\t', '\n', '\v', '\f', '\r',
							 | 
						|
								                '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', '\u0006', '\u000e', '\u000f',
							 | 
						|
								                '\u0010', '\u0011', '\u0012', '\u0013', '\u0014', '\u0015', '\u0016', '\u0017',
							 | 
						|
								                '\u0018', '\u0019', '\u001a', '\u001b', '\u001c', '\u001d', '\u001e', '\u001f'
							 | 
						|
								            };
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>合并路径。</summary>
							 | 
						|
								        public static string CombinePath(params string[] paths)
							 | 
						|
								        {
							 | 
						|
								            if (paths == null || paths.Length < 1) return "";
							 | 
						|
								            try
							 | 
						|
								            {
							 | 
						|
								#if NET20
							 | 
						|
								                var result = paths[0];
							 | 
						|
								                for (var i = 0; i < paths.Length; i++)
							 | 
						|
								                {
							 | 
						|
								                    result = Path.Combine(result, paths[i]);
							 | 
						|
								                }
							 | 
						|
								                return result;
							 | 
						|
								#else
							 | 
						|
								                return Path.Combine(paths);
							 | 
						|
								#endif
							 | 
						|
								            }
							 | 
						|
								            catch { return ""; }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>获取文件或目录的存在状态。</summary>
							 | 
						|
								        public static bool PathExists(string path)
							 | 
						|
								        {
							 | 
						|
								            if (string.IsNullOrEmpty(path)) return false;
							 | 
						|
								            if (File.Exists(path)) return true;
							 | 
						|
								            if (Directory.Exists(path)) return true;
							 | 
						|
								            return false;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>获取目录的存在状态。</summary>
							 | 
						|
								        public static bool DirectoryExists(string path) => string.IsNullOrEmpty(path) ? false : Directory.Exists(path);
							 | 
						|
								
							 | 
						|
								        /// <summary>获取文件的存在状态。</summary>
							 | 
						|
								        public static bool FileExists(string path) => string.IsNullOrEmpty(path) ? false : File.Exists(path);
							 | 
						|
								
							 | 
						|
								        /// <summary>获取文件或目录的上级目录完整路径,失败时返回 NULL 值。。</summary>
							 | 
						|
								        public static string GetParentPath(string path)
							 | 
						|
								        {
							 | 
						|
								            try
							 | 
						|
								            {
							 | 
						|
								                if (File.Exists(path)) return Path.GetDirectoryName(path);
							 | 
						|
								                if (Directory.Exists(path)) return Directory.GetParent(path).FullName;
							 | 
						|
								            }
							 | 
						|
								            catch { }
							 | 
						|
								            return null;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>修正文件名,去除不允许的字符。</summary>
							 | 
						|
								        public static string FixFileName(string fileName)
							 | 
						|
								        {
							 | 
						|
								            var result = fileName;
							 | 
						|
								            if (string.IsNullOrEmpty(result))
							 | 
						|
								            {
							 | 
						|
								                result = Constant.EmptyString;
							 | 
						|
								            }
							 | 
						|
								            else
							 | 
						|
								            {
							 | 
						|
								                var invalid = InvalidPathChars;
							 | 
						|
								                foreach (var c in invalid) result = result.Replace(c.ToString(), "");
							 | 
						|
								            }
							 | 
						|
								            if (result.Length > 0) result = result.Trim();
							 | 
						|
								            return result;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>获取指定目录的子目录,可指定递归子目录。</summary>
							 | 
						|
								        public static List<string> GetSubDirectories(string dirPath, bool recursive = false, bool recurPrecedence = true) => GetSubDirectories(dirPath, recursive ? -1 : 0, recurPrecedence);
							 | 
						|
								
							 | 
						|
								        /// <summary>获取指定目录下子目录的路径。</summary>
							 | 
						|
								        /// <param name="dirPath">顶级目录。</param>
							 | 
						|
								        /// <param name="recurDepth">子目录递归深度,指定为 0 时不递归,指定为 -1 时无限递归。</param>
							 | 
						|
								        /// <param name="recurPrecedence">优先排列递归项。</param>
							 | 
						|
								        public static List<string> GetSubDirectories(string dirPath, int recurDepth, bool recurPrecedence = true)
							 | 
						|
								        {
							 | 
						|
								            var list = new List<string>();
							 | 
						|
								            if (string.IsNullOrEmpty(dirPath)) return list;
							 | 
						|
								
							 | 
						|
								            var directories = new string[0];
							 | 
						|
								            try
							 | 
						|
								            {
							 | 
						|
								                directories = Directory.GetDirectories(dirPath);
							 | 
						|
								            }
							 | 
						|
								            catch { }
							 | 
						|
								
							 | 
						|
								            foreach (var dir in directories)
							 | 
						|
								            {
							 | 
						|
								                var recurlist = new List<string>();
							 | 
						|
								                if (recurDepth != 0)
							 | 
						|
								                {
							 | 
						|
								                    var depth = (recurDepth > 0) ? recurDepth - 1 : recurDepth;
							 | 
						|
								                    var subrecur = GetSubDirectories(dir, depth, recurPrecedence);
							 | 
						|
								                    recurlist.AddRange(subrecur);
							 | 
						|
								                }
							 | 
						|
								
							 | 
						|
								                if (recurPrecedence)
							 | 
						|
								                {
							 | 
						|
								                    list.AddRange(recurlist);
							 | 
						|
								                    list.Add(dir);
							 | 
						|
								                }
							 | 
						|
								                else
							 | 
						|
								                {
							 | 
						|
								                    list.Add(dir);
							 | 
						|
								                    list.AddRange(recurlist);
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            return list;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>获取指定目录的子文件。</summary>
							 | 
						|
								        /// <param name="dirPath">要查询的目录。</param>
							 | 
						|
								        /// <param name="recursive">递归子目录。</param>
							 | 
						|
								        /// <param name="recurPrecedence">优先排列递归项。</param>
							 | 
						|
								        public static List<string> GetSubFiles(string dirPath, bool recursive = false, bool recurPrecedence = true) => GetSubFiles(dirPath, recursive ? -1 : 0, recurPrecedence);
							 | 
						|
								
							 | 
						|
								        /// <summary>获取指定目录下子文件的路径。</summary>
							 | 
						|
								        /// <param name="dirPath">要查询的目录。</param>
							 | 
						|
								        /// <param name="recurDepth">子目录递归深度,指定为 0 时不递归,指定为 -1 时无限递归。</param>
							 | 
						|
								        /// <param name="recurPrecedence">优先排列递归项。</param>
							 | 
						|
								        public static List<string> GetSubFiles(string dirPath, int recurDepth, bool recurPrecedence = true)
							 | 
						|
								        {
							 | 
						|
								            var list = new List<string>();
							 | 
						|
								            if (string.IsNullOrEmpty(dirPath)) return list;
							 | 
						|
								
							 | 
						|
								            var dirs = new List<string>();
							 | 
						|
								            if (recurDepth == 0)
							 | 
						|
								            {
							 | 
						|
								                dirs.Add(dirPath);
							 | 
						|
								            }
							 | 
						|
								            else
							 | 
						|
								            {
							 | 
						|
								                var recurdicrotylist = GetSubDirectories(dirPath, recurDepth, recurPrecedence);
							 | 
						|
								                if (recurPrecedence)
							 | 
						|
								                {
							 | 
						|
								                    dirs.AddRange(recurdicrotylist);
							 | 
						|
								                    dirs.Add(dirPath);
							 | 
						|
								                }
							 | 
						|
								                else
							 | 
						|
								                {
							 | 
						|
								                    dirs.Add(dirPath);
							 | 
						|
								                    dirs.AddRange(recurdicrotylist);
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            foreach (var d in dirs)
							 | 
						|
								            {
							 | 
						|
								                try
							 | 
						|
								                {
							 | 
						|
								                    var files = System.IO.Directory.GetFiles(d);
							 | 
						|
								                    list.AddRange(files);
							 | 
						|
								                }
							 | 
						|
								                catch { }
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            return list;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        #endregion
							 | 
						|
								
							 | 
						|
								        #region directory
							 | 
						|
								
							 | 
						|
								        /// <summary>确信指定存在指定目录。</summary>
							 | 
						|
								        public static bool AssureDirectory(string path)
							 | 
						|
								        {
							 | 
						|
								            if (string.IsNullOrEmpty(path)) return false;
							 | 
						|
								            try
							 | 
						|
								            {
							 | 
						|
								                if (File.Exists(path)) return false;
							 | 
						|
								                if (Directory.Exists(path)) return true;
							 | 
						|
								                var created = Directory.CreateDirectory(path);
							 | 
						|
								                return created.Exists;
							 | 
						|
								            }
							 | 
						|
								            catch { }
							 | 
						|
								            return false;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>确信指定存在指定路径所在的上级目录。</summary>
							 | 
						|
								        public static bool AssureParent(string path)
							 | 
						|
								        {
							 | 
						|
								            if (string.IsNullOrEmpty(path)) return false;
							 | 
						|
								            var parent = Constant.EmptyString;
							 | 
						|
								            try { parent = Directory.GetParent(path).FullName; } catch { }
							 | 
						|
								            var result = AssureDirectory(parent);
							 | 
						|
								            return result;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        #endregion
							 | 
						|
								
							 | 
						|
								        #region file
							 | 
						|
								
							 | 
						|
								        /// <summary>打开文件,并获取文件流。若文件不存在,则先创建文件;若获取失败,则返回 NULL 值。可选文件的锁定状态。</summary>
							 | 
						|
								        public static FileStream OpenFile(string path, bool share = true)
							 | 
						|
								        {
							 | 
						|
								            try
							 | 
						|
								            {
							 | 
						|
								                if (string.IsNullOrEmpty(path)) return null;
							 | 
						|
								                if (DirectoryExists(path)) return null;
							 | 
						|
								
							 | 
						|
								                if (AssureParent(path))
							 | 
						|
								                {
							 | 
						|
								                    var m = FileMode.OpenOrCreate;
							 | 
						|
								                    var a = FileAccess.ReadWrite;
							 | 
						|
								                    var s = share ? FileShare.ReadWrite : FileShare.None;
							 | 
						|
								                    var stream = new FileStream(path, m, a, s);
							 | 
						|
								                    return stream;
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								            catch { }
							 | 
						|
								            return null;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>确保指定路径存在文件,若不存在则新建。</summary>
							 | 
						|
								        /// <param name="path">文件路径。</param>
							 | 
						|
								        public static bool AssureFile(string path)
							 | 
						|
								        {
							 | 
						|
								            if (string.IsNullOrEmpty(path)) return false;
							 | 
						|
								            if (File.Exists(path)) return true;
							 | 
						|
								            if (Directory.Exists(path)) return false;
							 | 
						|
								            if (!AssureParent(path)) return false;
							 | 
						|
								            try
							 | 
						|
								            {
							 | 
						|
								                File.Create(path).Dispose();
							 | 
						|
								                return true;
							 | 
						|
								            }
							 | 
						|
								            catch
							 | 
						|
								            {
							 | 
						|
								                return false;
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>创建一个空文件且不保留句柄。</summary>
							 | 
						|
								        /// <param name="path">文件路径,若已存在则返回失败。</param>
							 | 
						|
								        /// <param name="length">文件长度(字节数)。</param>
							 | 
						|
								        /// <param name="replace">替换现有文件。</param>
							 | 
						|
								        /// <returns>创建成功。</returns>
							 | 
						|
								        public static bool CreateFile(string path, long length = 0, bool replace = false)
							 | 
						|
								        {
							 | 
						|
								            if (string.IsNullOrEmpty(path)) return false;
							 | 
						|
								            if (!replace && File.Exists(path)) return false;
							 | 
						|
								            if (!AssureParent(path)) return false;
							 | 
						|
								            lock (Locker)
							 | 
						|
								            {
							 | 
						|
								                var file = null as FileStream;
							 | 
						|
								                var success = false;
							 | 
						|
								                try
							 | 
						|
								                {
							 | 
						|
								                    var m = replace ? FileMode.Create : FileMode.OpenOrCreate;
							 | 
						|
								                    file = new FileStream(path, m, FileAccess.ReadWrite, FileShare.ReadWrite);
							 | 
						|
								                    if (length > 0) file.SetLength(length);
							 | 
						|
								                    success = true;
							 | 
						|
								                }
							 | 
						|
								                catch { }
							 | 
						|
								                RuntimeUtility.Dispose(file);
							 | 
						|
								                return success;
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>复制文件。</summary>
							 | 
						|
								        /// <param name="source">旧路径。</param>
							 | 
						|
								        /// <param name="destination">新路径。</param>
							 | 
						|
								        /// <param name="replace">新路径存在时,替换新文件。</param>
							 | 
						|
								        public static bool CopyFile(string source, string destination, bool replace = true)
							 | 
						|
								        {
							 | 
						|
								            if (string.IsNullOrEmpty(source)) return false;
							 | 
						|
								            if (string.IsNullOrEmpty(destination)) return false;
							 | 
						|
								            lock (Locker)
							 | 
						|
								            {
							 | 
						|
								                if (!FileExists(source)) return false;
							 | 
						|
								                try
							 | 
						|
								                {
							 | 
						|
								                    File.Copy(source, destination, replace);
							 | 
						|
								                    return true;
							 | 
						|
								                }
							 | 
						|
								                catch { return false; }
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>向文件追加数据。文件不存在时将创建。</summary>
							 | 
						|
								        public static bool AppendFile(string path, params byte[] bytes)
							 | 
						|
								        {
							 | 
						|
								            if (bytes == null || bytes.Length < 1) return false;
							 | 
						|
								            lock (Locker)
							 | 
						|
								            {
							 | 
						|
								                var assured = AssureParent(path);
							 | 
						|
								                if (!assured) return false;
							 | 
						|
								
							 | 
						|
								                using (var file = OpenFile(path, true))
							 | 
						|
								                {
							 | 
						|
								                    if (file == null) return false;
							 | 
						|
								                    try
							 | 
						|
								                    {
							 | 
						|
								                        file.Position = file.Length;
							 | 
						|
								                        file.Write(bytes, 0, bytes.Length);
							 | 
						|
								                        file.Flush();
							 | 
						|
								                        return true;
							 | 
						|
								                    }
							 | 
						|
								                    catch { return false; }
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>将数据写入新文件,若文件已存在则覆盖。</summary>
							 | 
						|
								        public static bool WriteFile(string path, params byte[] bytes) => WriteFile(path, false, bytes);
							 | 
						|
								
							 | 
						|
								        /// <summary>将数据写入新文件,若文件已存在则覆盖。</summary>
							 | 
						|
								        public static bool WriteFile(string path, bool bom, params byte[] bytes)
							 | 
						|
								        {
							 | 
						|
								            if (string.IsNullOrEmpty(path)) return false;
							 | 
						|
								            if (bom)
							 | 
						|
								            {
							 | 
						|
								                lock (Locker)
							 | 
						|
								                {
							 | 
						|
								                    if (bytes == null || bytes.Length < 1)
							 | 
						|
								                    {
							 | 
						|
								                        try
							 | 
						|
								                        {
							 | 
						|
								                            var dir = Path.GetDirectoryName(path);
							 | 
						|
								                            if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
							 | 
						|
								
							 | 
						|
								                            File.WriteAllBytes(path, TextUtility.Bom);
							 | 
						|
								                            return true;
							 | 
						|
								                        }
							 | 
						|
								                        catch
							 | 
						|
								                        {
							 | 
						|
								                            return false;
							 | 
						|
								                        }
							 | 
						|
								                    }
							 | 
						|
								                    else
							 | 
						|
								                    {
							 | 
						|
								                        var file = OpenFile(path, true);
							 | 
						|
								                        var success = false;
							 | 
						|
								                        try
							 | 
						|
								                        {
							 | 
						|
								                            var dir = Path.GetDirectoryName(path);
							 | 
						|
								                            if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
							 | 
						|
								
							 | 
						|
								                            var write1 = BytesUtility.Write(file, TextUtility.Bom);
							 | 
						|
								                            if (write1 == TextUtility.Bom.Length)
							 | 
						|
								                            {
							 | 
						|
								                                var write2 = BytesUtility.Write(file, bytes);
							 | 
						|
								                                if (bytes != null && bytes.LongLength > 0L)
							 | 
						|
								                                {
							 | 
						|
								
							 | 
						|
								                                }
							 | 
						|
								                                success = true;
							 | 
						|
								                            }
							 | 
						|
								                        }
							 | 
						|
								                        catch { }
							 | 
						|
								                        RuntimeUtility.Dispose(file);
							 | 
						|
								                        return success;
							 | 
						|
								                    }
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								            else
							 | 
						|
								            {
							 | 
						|
								                lock (Locker)
							 | 
						|
								                {
							 | 
						|
								                    try
							 | 
						|
								                    {
							 | 
						|
								                        var dir = Path.GetDirectoryName(path);
							 | 
						|
								                        if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
							 | 
						|
								
							 | 
						|
								                        File.WriteAllBytes(path, bytes);
							 | 
						|
								                        return true;
							 | 
						|
								                    }
							 | 
						|
								                    catch
							 | 
						|
								                    {
							 | 
						|
								                        return false;
							 | 
						|
								                    }
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>读取文件,获取文件内容。当文件为 UTF-8 文本文件时,可去除 BOM 头。</summary>
							 | 
						|
								        /// <remarks>注:<br />字节数组最大为 2GB;<br />此方法不抛出异常,读取失时返回空字节数组。</remarks>
							 | 
						|
								        public static byte[] ReadFile(string path, bool wipeBom = false)
							 | 
						|
								        {
							 | 
						|
								            const int Buffer = 1048576;
							 | 
						|
								
							 | 
						|
								            if (!FileExists(path)) return Constant.EmptyBytes;
							 | 
						|
								            lock (Locker)
							 | 
						|
								            {
							 | 
						|
								                try
							 | 
						|
								                {
							 | 
						|
								                    var result = new byte[0];
							 | 
						|
								                    using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
							 | 
						|
								                    {
							 | 
						|
								                        if (!stream.CanRead) return result;
							 | 
						|
								                        stream.Position = 0;
							 | 
						|
								
							 | 
						|
								                        var length = stream.Length;
							 | 
						|
								                        if (length < 1) return result;
							 | 
						|
								                        if (length > int.MaxValue) return result;
							 | 
						|
								
							 | 
						|
								                        var offset = 0L;
							 | 
						|
								                        if (wipeBom && length >= 3)
							 | 
						|
								                        {
							 | 
						|
								                            var head = new byte[3];
							 | 
						|
								                            stream.Read(head, 0, 3);
							 | 
						|
								                            if (TextUtility.ContainsBOM(head))
							 | 
						|
								                            {
							 | 
						|
								                                var capacity = length - 3;
							 | 
						|
								                                result = new byte[capacity];
							 | 
						|
								                            }
							 | 
						|
								                            else
							 | 
						|
								                            {
							 | 
						|
								                                result = new byte[length];
							 | 
						|
								                                result[0] = head[0];
							 | 
						|
								                                result[1] = head[1];
							 | 
						|
								                                result[2] = head[2];
							 | 
						|
								                                offset = 3;
							 | 
						|
								                            }
							 | 
						|
								                        }
							 | 
						|
								                        else
							 | 
						|
								                        {
							 | 
						|
								                            result = new byte[length];
							 | 
						|
								                        }
							 | 
						|
								
							 | 
						|
								                        var block = new byte[Buffer];
							 | 
						|
								                        while (true)
							 | 
						|
								                        {
							 | 
						|
								                            var read = stream.Read(block, 0, Buffer);
							 | 
						|
								                            if (read < 1) break;
							 | 
						|
								
							 | 
						|
								                            Array.Copy(block, 0, result, offset, read);
							 | 
						|
								                            offset += read;
							 | 
						|
								                        }
							 | 
						|
								                    }
							 | 
						|
								                    return result;
							 | 
						|
								                }
							 | 
						|
								                catch (Exception ex)
							 | 
						|
								                {
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								            return Constant.EmptyBytes;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>获取指定文件的字节数,获取失败时返回 -1 值。</summary>
							 | 
						|
								        public static long GetFileLength(string path)
							 | 
						|
								        {
							 | 
						|
								            try
							 | 
						|
								            {
							 | 
						|
								                var info = new FileInfo(path);
							 | 
						|
								                return info.Length;
							 | 
						|
								            }
							 | 
						|
								            catch { }
							 | 
						|
								            return -1;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        #endregion
							 | 
						|
								
							 | 
						|
								        /// <summary>获取文件的最后写入时间。</summary>
							 | 
						|
								        public static DateTime GetFileLastWriteTime(string path, bool utc = false)
							 | 
						|
								        {
							 | 
						|
								            try
							 | 
						|
								            {
							 | 
						|
								                var info = new FileInfo(path);
							 | 
						|
								                return utc ? info.LastWriteTimeUtc : info.LastWriteTime;
							 | 
						|
								            }
							 | 
						|
								            catch { }
							 | 
						|
								            return new DateTime();
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>设置文件的最后写入时间。</summary>
							 | 
						|
								        public static string SetFileLastWriteTime(string path, DateTime value)
							 | 
						|
								        {
							 | 
						|
								            try
							 | 
						|
								            {
							 | 
						|
								                var info = new FileInfo(path);
							 | 
						|
								                info.LastWriteTime = value;
							 | 
						|
								                return null;
							 | 
						|
								            }
							 | 
						|
								            catch (Exception ex)
							 | 
						|
								            {
							 | 
						|
								                return ex.Message;
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>压缩文件到流。</summary>
							 | 
						|
								        /// <param name="files">Key 为 ZIP 内的文件名,Value 为原始文件路径。</param>
							 | 
						|
								        /// <param name="output">要输出的 ZIP 流。</param>
							 | 
						|
								        public static Exception ToZip(Dictionary<string, string> files, Stream output)
							 | 
						|
								        {
							 | 
						|
								            if (files == null) return new ArgumentNullException(nameof(files));
							 | 
						|
								
							 | 
						|
								            var streams = new List<Stream>();
							 | 
						|
								            var ex = BytesUtility.ToZip(files.Keys, output, (name) =>
							 | 
						|
								            {
							 | 
						|
								                if (name.IsEmpty()) return null;
							 | 
						|
								
							 | 
						|
								                var path = files[name];
							 | 
						|
								                if (path.IsEmpty()) return null;
							 | 
						|
								                if (!FileExists(path)) return null;
							 | 
						|
								
							 | 
						|
								                var input = OpenFile(path);
							 | 
						|
								                streams.Add(input);
							 | 
						|
								                return input;
							 | 
						|
								            }, (name) => GetFileLastWriteTime(files[name]), true);
							 | 
						|
								            RuntimeUtility.Dispose(streams);
							 | 
						|
								            return ex;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>压缩文件到流。</summary>
							 | 
						|
								        /// <param name="files">Key 为 ZIP 内的文件名,Value 为原始文件路径。</param>
							 | 
						|
								        /// <param name="output">要输出的 ZIP 文件路径,已存在的文件将被删除。</param>
							 | 
						|
								        public static Exception ToZip(Dictionary<string, string> files, string output)
							 | 
						|
								        {
							 | 
						|
								            if (output.IsEmpty()) return new ArgumentException(nameof(output));
							 | 
						|
								            if (FileExists(output)) DeleteFile(output);
							 | 
						|
								            if (FileExists(output)) return new IOException();
							 | 
						|
								            var stream = OpenFile(output);
							 | 
						|
								            if (stream == null) return new IOException();
							 | 
						|
								            var ex = ToZip(files, stream);
							 | 
						|
								            RuntimeUtility.Dispose(stream, true);
							 | 
						|
								            return ex;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>压缩文件到流。</summary>
							 | 
						|
								        /// <param name="directory">原始文件目录。</param>
							 | 
						|
								        /// <param name="output">要输出的 ZIP 流。</param>
							 | 
						|
								        public static Exception ToZip(string directory, Stream output)
							 | 
						|
								        {
							 | 
						|
								            if (!DirectoryExists(directory)) return new DirectoryNotFoundException();
							 | 
						|
								
							 | 
						|
								            var dict = new Dictionary<string, string>();
							 | 
						|
								            foreach (var path in GetSubFiles(directory, true))
							 | 
						|
								            {
							 | 
						|
								                var name = path.Substring(directory.Length);
							 | 
						|
								                if (name.StartsWith("\\")) name = name.Substring(1);
							 | 
						|
								                if (name.StartsWith("/")) name = name.Substring(1);
							 | 
						|
								                dict.Add(name, path);
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            return ToZip(dict, output);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>压缩文件到流。</summary>
							 | 
						|
								        /// <param name="directory">原始文件目录。</param>
							 | 
						|
								        /// <param name="output">要输出的 ZIP 文件路径,已存在的旧文件将被删除。</param>
							 | 
						|
								        public static Exception ToZip(string directory, string output)
							 | 
						|
								        {
							 | 
						|
								            if (output.IsEmpty()) return new ArgumentException(nameof(output));
							 | 
						|
								            if (FileExists(output)) DeleteFile(output);
							 | 
						|
								            if (FileExists(output)) return new IOException("无法删除现有文件。");
							 | 
						|
								            var stream = OpenFile(output);
							 | 
						|
								            if (stream == null) return new IOException("无法创建文件。");
							 | 
						|
								            var ex = ToZip(directory, stream);
							 | 
						|
								            RuntimeUtility.Dispose(stream, true);
							 | 
						|
								            return ex;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>解压 ZIP 文件到目标目录。已存在的旧文件将被删除。</summary>
							 | 
						|
								        public static Exception FromZip(string zipPath, string outputDirectory = null)
							 | 
						|
								        {
							 | 
						|
								            if (!FileExists(zipPath)) return new FileNotFoundException("指定的 ZIP 文件路径无效。");
							 | 
						|
								
							 | 
						|
								            var directory = outputDirectory;
							 | 
						|
								            if (directory.IsEmpty())
							 | 
						|
								            {
							 | 
						|
								                if (zipPath.Lower().EndsWith(".zip"))
							 | 
						|
								                {
							 | 
						|
								                    directory = zipPath.Substring(0, zipPath.Length - 4);
							 | 
						|
								                    //if (directory.EndsWith("/") || directory.EndsWith("\\"))
							 | 
						|
								                    //if (PathExists(directory)) directory = null;
							 | 
						|
								                }
							 | 
						|
								                else
							 | 
						|
								                {
							 | 
						|
								                    directory = GetParentPath(zipPath);
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								            if (directory.IsEmpty()) return new ArgumentException("无法判断目录路径。");
							 | 
						|
								            if (!AssureDirectory(directory)) return new DirectoryNotFoundException("无法创建目录。");
							 | 
						|
								
							 | 
						|
								            var input = OpenFile(zipPath);
							 | 
						|
								            if (input == null) return new IOException("无法打开 ZIP 文件。");
							 | 
						|
								            var info = new Dictionary<string, DateTime>();
							 | 
						|
								            var ex = BytesUtility.FromZip(input, (name, size, modifield) =>
							 | 
						|
								            {
							 | 
						|
								                var path = Path.Combine(directory, name);
							 | 
						|
								                if (FileExists(path)) DeleteFile(path);
							 | 
						|
								                if (DirectoryExists(path)) DeleteDirectory(path, true);
							 | 
						|
								                if (PathExists(path)) throw new IOException("无法删除旧文件或旧目录。");
							 | 
						|
								
							 | 
						|
								                var output = OpenFile(path);
							 | 
						|
								                if (output == null) throw new IOException("无法创建文件 " + path + "。");
							 | 
						|
								                if (info.ContainsKey(path))
							 | 
						|
								                {
							 | 
						|
								                    RuntimeUtility.Dispose(output);
							 | 
						|
								                    return null;
							 | 
						|
								                }
							 | 
						|
								                info.Add(path, modifield);
							 | 
						|
								                return output;
							 | 
						|
								            }, (name, modified) =>
							 | 
						|
								            {
							 | 
						|
								                var path = Path.Combine(directory, name);
							 | 
						|
								                var exists = AssureDirectory(path);
							 | 
						|
								                if (!exists) throw new IOException("无法创建 ZIP 中对应的目录。");
							 | 
						|
								            }, true);
							 | 
						|
								
							 | 
						|
								            // 恢复文件的修改时间。
							 | 
						|
								            foreach (var i in info) SetFileLastWriteTime(i.Key, i.Value);
							 | 
						|
								
							 | 
						|
								            return ex;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        ///// <summary>解压 ZIP 文件到字典。失败且不允许异常时返回 NULL 值。</summary>
							 | 
						|
								        //public static Dictionary<string, byte[]> FromZip(string zipPath, bool allowException = false)
							 | 
						|
								        //{
							 | 
						|
								        //    if (FileExists(zipPath))
							 | 
						|
								        //    {
							 | 
						|
								        //        var ex = new FileNotFoundException("文件不存在。");
							 | 
						|
								        //        if (allowException) throw ex;
							 | 
						|
								        //        return null;
							 | 
						|
								        //    }
							 | 
						|
								
							 | 
						|
								        //    var input = OpenFile(zipPath);
							 | 
						|
								        //    if (input == null)
							 | 
						|
								        //    {
							 | 
						|
								        //        var ex = new IOException("无法打开 ZIP 文件。");
							 | 
						|
								        //        if (allowException) throw ex;
							 | 
						|
								        //        return null;
							 | 
						|
								        //    }
							 | 
						|
								
							 | 
						|
								        //    return BinaryUtility.FromZip(input, ;
							 | 
						|
								        //} 
							 | 
						|
								
							 | 
						|
								        /// <summary>获取指定程序集的资源。</summary>
							 | 
						|
								        public static Stream GetResource(string name, Assembly assembly = null)
							 | 
						|
								        {
							 | 
						|
								            try
							 | 
						|
								            {
							 | 
						|
								                if (assembly == null) assembly = Assembly.GetExecutingAssembly();
							 | 
						|
								                var stream = assembly.GetManifestResourceStream(name);
							 | 
						|
								                return stream;
							 | 
						|
								            }
							 | 
						|
								            catch { }
							 | 
						|
								            return null;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>获取当前进程程序集的资源。读取失败时返回 NULL 值。</summary>
							 | 
						|
								        public static byte[] ReadResource(string name, Assembly assembly = null)
							 | 
						|
								        {
							 | 
						|
								            var stream = GetResource(name, assembly);
							 | 
						|
								            if (stream == null) return null;
							 | 
						|
								            var bytes = BytesUtility.Read(stream, true);
							 | 
						|
								            return bytes;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>监视文件夹的变化。</summary>
							 | 
						|
								        public static FileSystemWatcher NewFileSystemWatcher(string directory, FileSystemEventHandler action)
							 | 
						|
								        {
							 | 
						|
								            var watcher = new FileSystemWatcher();
							 | 
						|
								            watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite | NotifyFilters.Size;
							 | 
						|
								            watcher.IncludeSubdirectories = true;
							 | 
						|
								            watcher.Path = directory;
							 | 
						|
								
							 | 
						|
								            watcher.Created += (s, e) => action?.Invoke(watcher, e);
							 | 
						|
								            watcher.Deleted += (s, e) => action?.Invoke(watcher, e);
							 | 
						|
								            watcher.Changed += (s, e) => action?.Invoke(watcher, e);
							 | 
						|
								            watcher.Renamed += (s, e) => action?.Invoke(watcher, e);
							 | 
						|
								            watcher.EnableRaisingEvents = true;
							 | 
						|
								
							 | 
						|
								            return watcher;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 |