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.

795 lines
30 KiB

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