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.

766 lines
29 KiB

3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. using Apewer.Internals;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Reflection;
  6. using System.Security.Permissions;
  7. namespace Apewer
  8. {
  9. /// <summary>存储实用工具。</summary>
  10. public static class StorageUtility
  11. {
  12. private static Exception Try(Action action)
  13. {
  14. try { action?.Invoke(); return null; }
  15. catch (Exception ex) { return ex; }
  16. }
  17. /// <summary>文件操作线程锁。</summary>
  18. public static object Locker = new object();
  19. #region NTFS 流
  20. /// <summary>解锁下载的文件。</summary>
  21. /// <remarks>此方法仅支持 Windows 系统。</remarks>
  22. public static string DeleteZoneIdentifier(string path) => NtfsUnlocker.DeleteZoneIdentifier(path);
  23. #endregion
  24. #region delete
  25. /// <summary>删除文件。</summary>
  26. public static Exception DeleteFile(string path, bool useTemp = false)
  27. {
  28. if (useTemp)
  29. {
  30. try
  31. {
  32. if (string.IsNullOrEmpty(path)) return new ArgumentException();
  33. if (!File.Exists(path)) return new FileNotFoundException();
  34. Try(() => new FileInfo(path).Attributes = FileAttributes.Normal);
  35. var temp = Environment.GetEnvironmentVariable("TEMP");
  36. var name = Path.GetFileName(path);
  37. var dest = null as string;
  38. while (dest == null || File.Exists(dest))
  39. {
  40. var guid = Guid.NewGuid().ToString("n").Substring(0, 8);
  41. dest = $"trash_{guid}_{name}";
  42. }
  43. File.Move(path, dest);
  44. File.Delete(dest);
  45. return null;
  46. }
  47. catch (Exception ex) { return ex; }
  48. }
  49. try
  50. {
  51. File.Delete(path);
  52. return null;
  53. }
  54. catch (Exception ex) { return ex; }
  55. }
  56. /// <summary>删除目录、子目录和子文件。</summary>
  57. public static Exception DeleteDirectory(string path, bool useTemp = false)
  58. {
  59. if (useTemp)
  60. {
  61. try
  62. {
  63. if (string.IsNullOrEmpty(path)) return new ArgumentException();
  64. if (!Directory.Exists(path)) return new DirectoryNotFoundException();
  65. var temp = Environment.GetEnvironmentVariable("TEMP");
  66. var name = Path.GetDirectoryName(path);
  67. var dest = null as string;
  68. while (dest == null || Directory.Exists(dest))
  69. {
  70. var guid = Guid.NewGuid().ToString("n").Substring(0, 8);
  71. dest = $"trash_{guid}_{name}";
  72. }
  73. Directory.Move(path, dest);
  74. Directory.Delete(dest, true);
  75. return null;
  76. }
  77. catch (Exception ex) { return ex; }
  78. }
  79. try
  80. {
  81. Directory.Delete(path, true);
  82. return null;
  83. }
  84. catch (Exception ex) { return ex; }
  85. }
  86. #endregion
  87. #region path
  88. /// <summary>无效的路径字符。</summary>
  89. public static char[] InvalidPathChars
  90. {
  91. // SMB 共享文件夹无效字符
  92. // ! " # % & ' ( ) * + , / : ; < = > ? @ [ ] \ ^ ` { } | ~
  93. get => new char[] {
  94. '\\', '/', '\'', '"', ':', '*', '?', '<', '>', '|',
  95. '\0', '\a', '\b', '\t', '\n', '\v', '\f', '\r',
  96. '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', '\u0006', '\u000e', '\u000f',
  97. '\u0010', '\u0011', '\u0012', '\u0013', '\u0014', '\u0015', '\u0016', '\u0017',
  98. '\u0018', '\u0019', '\u001a', '\u001b', '\u001c', '\u001d', '\u001e', '\u001f'
  99. };
  100. }
  101. /// <summary>合并路径。</summary>
  102. public static string CombinePath(params string[] paths)
  103. {
  104. if (paths == null || paths.Length < 1) return "";
  105. try
  106. {
  107. #if NET20
  108. var result = paths[0];
  109. for (var i = 0; i < paths.Length; i++)
  110. {
  111. result = Path.Combine(result, paths[i]);
  112. }
  113. return result;
  114. #else
  115. return Path.Combine(paths);
  116. #endif
  117. }
  118. catch { return ""; }
  119. }
  120. /// <summary>获取文件或目录的存在状态。</summary>
  121. public static bool PathExists(string path)
  122. {
  123. if (string.IsNullOrEmpty(path)) return false;
  124. if (File.Exists(path)) return true;
  125. if (Directory.Exists(path)) return true;
  126. return false;
  127. }
  128. /// <summary>获取目录的存在状态。</summary>
  129. public static bool DirectoryExists(string path) => string.IsNullOrEmpty(path) ? false : Directory.Exists(path);
  130. /// <summary>获取文件的存在状态。</summary>
  131. public static bool FileExists(string path) => string.IsNullOrEmpty(path) ? false : File.Exists(path);
  132. /// <summary>获取文件或目录的上级目录完整路径,失败时返回 NULL 值。。</summary>
  133. public static string GetParentPath(string path)
  134. {
  135. try
  136. {
  137. if (File.Exists(path)) return Path.GetDirectoryName(path);
  138. if (Directory.Exists(path)) return Directory.GetParent(path).FullName;
  139. }
  140. catch { }
  141. return null;
  142. }
  143. /// <summary>修正文件名,去除不允许的字符。</summary>
  144. public static string FixFileName(string fileName)
  145. {
  146. var result = fileName;
  147. if (string.IsNullOrEmpty(result))
  148. {
  149. result = Constant.EmptyString;
  150. }
  151. else
  152. {
  153. var invalid = InvalidPathChars;
  154. foreach (var c in invalid) result = result.Replace(c.ToString(), "");
  155. }
  156. if (result.Length > 0) result = result.Trim();
  157. return result;
  158. }
  159. /// <summary>获取指定目录的子目录,可指定递归子目录。</summary>
  160. public static List<string> GetSubDirectories(string dirPath, bool recursive = false, bool recurPrecedence = true) => GetSubDirectories(dirPath, recursive ? -1 : 0, recurPrecedence);
  161. /// <summary>获取指定目录下子目录的路径。</summary>
  162. /// <param name="dirPath">顶级目录。</param>
  163. /// <param name="recurDepth">子目录递归深度,指定为 0 时不递归,指定为 -1 时无限递归。</param>
  164. /// <param name="recurPrecedence">优先排列递归项。</param>
  165. public static List<string> GetSubDirectories(string dirPath, int recurDepth, bool recurPrecedence = true)
  166. {
  167. var list = new List<string>();
  168. if (string.IsNullOrEmpty(dirPath)) return list;
  169. var directories = new string[0];
  170. try
  171. {
  172. directories = Directory.GetDirectories(dirPath);
  173. }
  174. catch { }
  175. foreach (var dir in directories)
  176. {
  177. var recurlist = new List<string>();
  178. if (recurDepth != 0)
  179. {
  180. var depth = (recurDepth > 0) ? recurDepth - 1 : recurDepth;
  181. var subrecur = GetSubDirectories(dir, depth, recurPrecedence);
  182. recurlist.AddRange(subrecur);
  183. }
  184. if (recurPrecedence)
  185. {
  186. list.AddRange(recurlist);
  187. list.Add(dir);
  188. }
  189. else
  190. {
  191. list.Add(dir);
  192. list.AddRange(recurlist);
  193. }
  194. }
  195. return list;
  196. }
  197. /// <summary>获取指定目录的子文件。</summary>
  198. /// <param name="dirPath">要查询的目录。</param>
  199. /// <param name="recursive">递归子目录。</param>
  200. /// <param name="recurPrecedence">优先排列递归项。</param>
  201. public static List<string> GetSubFiles(string dirPath, bool recursive = false, bool recurPrecedence = true) => GetSubFiles(dirPath, recursive ? -1 : 0, recurPrecedence);
  202. /// <summary>获取指定目录下子文件的路径。</summary>
  203. /// <param name="dirPath">要查询的目录。</param>
  204. /// <param name="recurDepth">子目录递归深度,指定为 0 时不递归,指定为 -1 时无限递归。</param>
  205. /// <param name="recurPrecedence">优先排列递归项。</param>
  206. public static List<string> GetSubFiles(string dirPath, int recurDepth, bool recurPrecedence = true)
  207. {
  208. var list = new List<string>();
  209. if (string.IsNullOrEmpty(dirPath)) return list;
  210. var dirs = new List<string>();
  211. if (recurDepth == 0)
  212. {
  213. dirs.Add(dirPath);
  214. }
  215. else
  216. {
  217. var recurdicrotylist = GetSubDirectories(dirPath, recurDepth, recurPrecedence);
  218. if (recurPrecedence)
  219. {
  220. dirs.AddRange(recurdicrotylist);
  221. dirs.Add(dirPath);
  222. }
  223. else
  224. {
  225. dirs.Add(dirPath);
  226. dirs.AddRange(recurdicrotylist);
  227. }
  228. }
  229. foreach (var d in dirs)
  230. {
  231. try
  232. {
  233. var files = System.IO.Directory.GetFiles(d);
  234. list.AddRange(files);
  235. }
  236. catch { }
  237. }
  238. return list;
  239. }
  240. #endregion
  241. #region directory
  242. /// <summary>确信指定存在指定目录。</summary>
  243. public static bool AssureDirectory(string path)
  244. {
  245. if (string.IsNullOrEmpty(path)) return false;
  246. try
  247. {
  248. if (File.Exists(path)) return false;
  249. if (Directory.Exists(path)) return true;
  250. var created = Directory.CreateDirectory(path);
  251. return created.Exists;
  252. }
  253. catch { }
  254. return false;
  255. }
  256. /// <summary>确信指定存在指定路径所在的上级目录。</summary>
  257. public static bool AssureParent(string path)
  258. {
  259. if (string.IsNullOrEmpty(path)) return false;
  260. var parent = Constant.EmptyString;
  261. try { parent = Directory.GetParent(path).FullName; } catch { }
  262. var result = AssureDirectory(parent);
  263. return result;
  264. }
  265. #endregion
  266. #region file
  267. /// <summary>打开文件,并获取文件流。若文件不存在,则先创建文件;若获取失败,则返回 NULL 值。可选文件的锁定状态。</summary>
  268. public static FileStream OpenFile(string path, bool share = true)
  269. {
  270. try
  271. {
  272. if (string.IsNullOrEmpty(path)) return null;
  273. if (DirectoryExists(path)) return null;
  274. if (AssureParent(path))
  275. {
  276. var m = FileMode.OpenOrCreate;
  277. var a = FileAccess.ReadWrite;
  278. var s = share ? FileShare.ReadWrite : FileShare.None;
  279. var stream = new FileStream(path, m, a, s);
  280. return stream;
  281. }
  282. }
  283. catch { }
  284. return null;
  285. }
  286. /// <summary>创建一个空文件且不保留句柄。</summary>
  287. /// <param name="path">文件路径,若已存在则返回失败。</param>
  288. /// <param name="length">文件长度(字节数)。</param>
  289. /// <param name="replace">替换现有文件。</param>
  290. /// <returns>创建成功。</returns>
  291. public static bool CreateFile(string path, long length = 0, bool replace = false)
  292. {
  293. if (string.IsNullOrEmpty(path)) return false;
  294. if (!replace && File.Exists(path)) return false;
  295. if (!AssureParent(path)) return false;
  296. lock (Locker)
  297. {
  298. var file = null as FileStream;
  299. var success = false;
  300. try
  301. {
  302. var m = replace ? FileMode.Create : FileMode.OpenOrCreate;
  303. file = new FileStream(path, m, FileAccess.ReadWrite, FileShare.ReadWrite);
  304. if (length > 0) file.SetLength(length);
  305. success = true;
  306. }
  307. catch { }
  308. RuntimeUtility.Dispose(file);
  309. return success;
  310. }
  311. }
  312. /// <summary>复制文件。</summary>
  313. /// <param name="source">旧路径。</param>
  314. /// <param name="destination">新路径。</param>
  315. /// <param name="replace">新路径存在时,替换新文件。</param>
  316. public static bool CopyFile(string source, string destination, bool replace = true)
  317. {
  318. if (string.IsNullOrEmpty(source)) return false;
  319. if (string.IsNullOrEmpty(destination)) return false;
  320. lock (Locker)
  321. {
  322. if (!FileExists(source)) return false;
  323. try
  324. {
  325. File.Copy(source, destination, replace);
  326. return true;
  327. }
  328. catch { return false; }
  329. }
  330. }
  331. /// <summary>向文件追加数据。文件不存在时将创建。</summary>
  332. public static bool AppendFile(string path, params byte[] bytes)
  333. {
  334. if (bytes == null || bytes.Length < 1) return false;
  335. lock (Locker)
  336. {
  337. var assured = AssureParent(path);
  338. if (!assured) return false;
  339. using (var file = OpenFile(path, true))
  340. {
  341. if (file == null) return false;
  342. try
  343. {
  344. file.Position = file.Length;
  345. file.Write(bytes, 0, bytes.Length);
  346. file.Flush();
  347. return true;
  348. }
  349. catch { return false; }
  350. }
  351. }
  352. }
  353. /// <summary>将数据写入新文件,若文件已存在则覆盖。</summary>
  354. public static bool WriteFile(string path, params byte[] bytes) => WriteFile(path, false, bytes);
  355. /// <summary>将数据写入新文件,若文件已存在则覆盖。</summary>
  356. public static bool WriteFile(string path, bool bom, params byte[] bytes)
  357. {
  358. if (string.IsNullOrEmpty(path)) return false;
  359. if (bom)
  360. {
  361. lock (Locker)
  362. {
  363. if (bytes == null || bytes.Length < 1)
  364. {
  365. try
  366. {
  367. File.WriteAllBytes(path, TextUtility.Bom);
  368. return true;
  369. }
  370. catch
  371. {
  372. return false;
  373. }
  374. }
  375. else
  376. {
  377. var file = OpenFile(path, true);
  378. var success = false;
  379. try
  380. {
  381. var write1 = BytesUtility.Write(file, TextUtility.Bom);
  382. if (write1 == TextUtility.Bom.Length)
  383. {
  384. var write2 = BytesUtility.Write(file, bytes);
  385. if (bytes != null && bytes.LongLength > 0L)
  386. {
  387. }
  388. success = true;
  389. }
  390. }
  391. catch { }
  392. RuntimeUtility.Dispose(file);
  393. return success;
  394. }
  395. }
  396. }
  397. else
  398. {
  399. lock (Locker)
  400. {
  401. try
  402. {
  403. File.WriteAllBytes(path, bytes);
  404. return true;
  405. }
  406. catch
  407. {
  408. return false;
  409. }
  410. }
  411. }
  412. }
  413. /// <summary>将数据写入新文件,若文件已存在则覆盖。</summary>
  414. public static bool WriteFile(string path, Json json, bool bom = false)
  415. {
  416. if (string.IsNullOrEmpty(path)) return false;
  417. if (json == null || json.IsNull || json.IsNone) return false;
  418. var text = json.ToString(true);
  419. var bytes = TextUtility.Bytes(text);
  420. return WriteFile(path, bom, bytes);
  421. }
  422. /// <summary>读取文件,获取文件内容。当文件为 UTF-8 文本文件时,可去除 BOM 头。</summary>
  423. /// <remarks>注:<br />字节数组最大为 2GB;<br />此方法不抛出异常,读取失时返回空字节数组。</remarks>
  424. public static byte[] ReadFile(string path, bool wipeBom = false)
  425. {
  426. const int Buffer = 1048576;
  427. if (!FileExists(path)) return Constant.EmptyBytes;
  428. lock (Locker)
  429. {
  430. try
  431. {
  432. var result = new byte[0];
  433. using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
  434. {
  435. if (!stream.CanRead) return result;
  436. stream.Position = 0;
  437. var length = stream.Length;
  438. if (length < 1) return result;
  439. if (length > int.MaxValue) return result;
  440. var offset = 0L;
  441. if (wipeBom && length >= 3)
  442. {
  443. var head = new byte[3];
  444. stream.Read(head, 0, 3);
  445. if (TextUtility.ContainsBOM(head))
  446. {
  447. var capacity = length - 3;
  448. result = new byte[capacity];
  449. }
  450. else
  451. {
  452. result = new byte[length];
  453. result[0] = head[0];
  454. result[1] = head[1];
  455. result[2] = head[2];
  456. offset = 3;
  457. }
  458. }
  459. else
  460. {
  461. result = new byte[length];
  462. }
  463. var block = new byte[Buffer];
  464. while (true)
  465. {
  466. var read = stream.Read(block, 0, Buffer);
  467. if (read < 1) break;
  468. Array.Copy(block, 0, result, offset, read);
  469. offset += read;
  470. }
  471. }
  472. return result;
  473. }
  474. catch { }
  475. }
  476. return Constant.EmptyBytes;
  477. }
  478. /// <summary>获取指定文件的字节数,获取失败时返回 -1 值。</summary>
  479. public static long GetFileLength(string path)
  480. {
  481. try
  482. {
  483. var info = new FileInfo(path);
  484. return info.Length;
  485. }
  486. catch { }
  487. return -1;
  488. }
  489. #endregion
  490. /// <summary>获取文件的最后写入时间。</summary>
  491. public static DateTime GetFileLastWriteTime(string path, bool utc = false)
  492. {
  493. try
  494. {
  495. var info = new FileInfo(path);
  496. return utc ? info.LastWriteTimeUtc : info.LastWriteTime;
  497. }
  498. catch { }
  499. return new DateTime();
  500. }
  501. /// <summary>设置文件的最后写入时间。</summary>
  502. public static string SetFileLastWriteTime(string path, DateTime value)
  503. {
  504. try
  505. {
  506. var info = new FileInfo(path);
  507. info.LastWriteTime = value;
  508. return null;
  509. }
  510. catch (Exception ex)
  511. {
  512. return ex.Message;
  513. }
  514. }
  515. /// <summary>压缩文件到流。</summary>
  516. /// <param name="files">Key 为 ZIP 内的文件名,Value 为原始文件路径。</param>
  517. /// <param name="output">要输出的 ZIP 流。</param>
  518. public static Exception ToZip(Dictionary<string, string> files, Stream output)
  519. {
  520. if (files == null) return new ArgumentNullException(nameof(files));
  521. var streams = new List<Stream>();
  522. var ex = BytesUtility.ToZip(files.Keys, output, (name) =>
  523. {
  524. if (name.IsEmpty()) return null;
  525. var path = files[name];
  526. if (path.IsEmpty()) return null;
  527. if (!FileExists(path)) return null;
  528. var input = OpenFile(path);
  529. streams.Add(input);
  530. return input;
  531. }, (name) => GetFileLastWriteTime(files[name]), true);
  532. RuntimeUtility.Dispose(streams);
  533. return ex;
  534. }
  535. /// <summary>压缩文件到流。</summary>
  536. /// <param name="files">Key 为 ZIP 内的文件名,Value 为原始文件路径。</param>
  537. /// <param name="output">要输出的 ZIP 文件路径,已存在的文件将被删除。</param>
  538. public static Exception ToZip(Dictionary<string, string> files, string output)
  539. {
  540. if (output.IsEmpty()) return new ArgumentException(nameof(output));
  541. if (FileExists(output)) DeleteFile(output);
  542. if (FileExists(output)) return new IOException();
  543. var stream = OpenFile(output);
  544. if (stream == null) return new IOException();
  545. var ex = ToZip(files, stream);
  546. RuntimeUtility.Dispose(stream, true);
  547. return ex;
  548. }
  549. /// <summary>压缩文件到流。</summary>
  550. /// <param name="directory">原始文件目录。</param>
  551. /// <param name="output">要输出的 ZIP 流。</param>
  552. public static Exception ToZip(string directory, Stream output)
  553. {
  554. if (!DirectoryExists(directory)) return new DirectoryNotFoundException();
  555. var dict = new Dictionary<string, string>();
  556. foreach (var path in GetSubFiles(directory, true))
  557. {
  558. var name = path.Substring(directory.Length);
  559. if (name.StartsWith("\\")) name = name.Substring(1);
  560. if (name.StartsWith("/")) name = name.Substring(1);
  561. dict.Add(name, path);
  562. }
  563. return ToZip(dict, output);
  564. }
  565. /// <summary>压缩文件到流。</summary>
  566. /// <param name="directory">原始文件目录。</param>
  567. /// <param name="output">要输出的 ZIP 文件路径,已存在的旧文件将被删除。</param>
  568. public static Exception ToZip(string directory, string output)
  569. {
  570. if (output.IsEmpty()) return new ArgumentException(nameof(output));
  571. if (FileExists(output)) DeleteFile(output);
  572. if (FileExists(output)) return new IOException("无法删除现有文件。");
  573. var stream = OpenFile(output);
  574. if (stream == null) return new IOException("无法创建文件。");
  575. var ex = ToZip(directory, stream);
  576. RuntimeUtility.Dispose(stream, true);
  577. return ex;
  578. }
  579. /// <summary>解压 ZIP 文件到目标目录。已存在的旧文件将被删除。</summary>
  580. public static Exception FromZip(string zipPath, string outputDirectory = null)
  581. {
  582. if (!FileExists(zipPath)) return new FileNotFoundException("指定的 ZIP 文件路径无效。");
  583. var directory = outputDirectory;
  584. if (directory.IsEmpty())
  585. {
  586. if (zipPath.Lower().EndsWith(".zip"))
  587. {
  588. directory = zipPath.Substring(0, zipPath.Length - 4);
  589. //if (directory.EndsWith("/") || directory.EndsWith("\\"))
  590. //if (PathExists(directory)) directory = null;
  591. }
  592. else
  593. {
  594. directory = GetParentPath(zipPath);
  595. }
  596. }
  597. if (directory.IsEmpty()) return new ArgumentException("无法判断目录路径。");
  598. if (!AssureDirectory(directory)) return new DirectoryNotFoundException("无法创建目录。");
  599. var input = OpenFile(zipPath);
  600. if (input == null) return new IOException("无法打开 ZIP 文件。");
  601. var info = new Dictionary<string, DateTime>();
  602. var ex = BytesUtility.FromZip(input, (name, size, modifield) =>
  603. {
  604. var path = Path.Combine(directory, name);
  605. if (FileExists(path)) DeleteFile(path);
  606. if (DirectoryExists(path)) DeleteDirectory(path, true);
  607. if (PathExists(path)) throw new IOException("无法删除旧文件或旧目录。");
  608. var output = OpenFile(path);
  609. if (output == null) throw new IOException("无法创建文件 " + path + "。");
  610. if (info.ContainsKey(path))
  611. {
  612. RuntimeUtility.Dispose(output);
  613. return null;
  614. }
  615. info.Add(path, modifield);
  616. return output;
  617. }, (name, modified) =>
  618. {
  619. var path = Path.Combine(directory, name);
  620. var exists = AssureDirectory(path);
  621. if (!exists) throw new IOException("无法创建 ZIP 中对应的目录。");
  622. }, true);
  623. // 恢复文件的修改时间。
  624. foreach (var i in info) SetFileLastWriteTime(i.Key, i.Value);
  625. return ex;
  626. }
  627. ///// <summary>解压 ZIP 文件到字典。失败且不允许异常时返回 NULL 值。</summary>
  628. //public static Dictionary<string, byte[]> FromZip(string zipPath, bool allowException = false)
  629. //{
  630. // if (FileExists(zipPath))
  631. // {
  632. // var ex = new FileNotFoundException("文件不存在。");
  633. // if (allowException) throw ex;
  634. // return null;
  635. // }
  636. // var input = OpenFile(zipPath);
  637. // if (input == null)
  638. // {
  639. // var ex = new IOException("无法打开 ZIP 文件。");
  640. // if (allowException) throw ex;
  641. // return null;
  642. // }
  643. // return BinaryUtility.FromZip(input, ;
  644. //}
  645. /// <summary>获取指定程序集的资源。</summary>
  646. public static Stream GetResource(string name, Assembly assembly = null)
  647. {
  648. try
  649. {
  650. if (assembly == null) assembly = Assembly.GetExecutingAssembly();
  651. var stream = assembly.GetManifestResourceStream(name);
  652. return stream;
  653. }
  654. catch { }
  655. return null;
  656. }
  657. /// <summary>获取当前进程程序集的资源。读取失败时返回 NULL 值。</summary>
  658. public static byte[] ReadResource(string name, Assembly assembly = null)
  659. {
  660. var stream = GetResource(name, assembly);
  661. if (stream == null) return null;
  662. var bytes = BytesUtility.Read(stream, true);
  663. return bytes;
  664. }
  665. /// <summary>监视文件夹的变化。</summary>
  666. public static FileSystemWatcher NewFileSystemWatcher(string directory, FileSystemEventHandler action)
  667. {
  668. var watcher = new FileSystemWatcher();
  669. watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite | NotifyFilters.Size;
  670. watcher.IncludeSubdirectories = true;
  671. watcher.Path = directory;
  672. watcher.Created += (s, e) => action?.Invoke(watcher, e);
  673. watcher.Deleted += (s, e) => action?.Invoke(watcher, e);
  674. watcher.Changed += (s, e) => action?.Invoke(watcher, e);
  675. watcher.Renamed += (s, e) => action?.Invoke(watcher, e);
  676. watcher.EnableRaisingEvents = true;
  677. return watcher;
  678. }
  679. }
  680. }