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.

994 lines
35 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.IO;
  5. using System.IO.Compression;
  6. using System.Reflection;
  7. using System.Security.Cryptography;
  8. using Externals.Compression.Zip;
  9. using Externals.Compression.Checksums;
  10. using Apewer.Internals;
  11. using System.Diagnostics;
  12. // using System.Runtime.Remoting.Messaging;
  13. namespace Apewer
  14. {
  15. /// <summary>二进制。</summary>
  16. public class BinaryUtility
  17. {
  18. /// <summary>空字节数组,每次获取都将创建新的引用。</summary>
  19. public static byte[] EmptyBytes { get { return Constant.EmptyBytes; } }
  20. #region Bytes Array
  21. /// <summary>创建数组,元素值为零。</summary>
  22. public static byte[] ZeroArray(int length = 0)
  23. {
  24. if (length < 1) return new byte[0];
  25. var array = new byte[length];
  26. for (int i = 0; i < length; i++) array[i] = 0;
  27. return array;
  28. }
  29. /// <summary>所有字节取反。</summary>
  30. public static byte[] Adverse(byte[] bytes)
  31. {
  32. if (bytes == null || bytes.Length < 1) return Constant.EmptyBytes;
  33. byte[] adverse = new byte[bytes.Length];
  34. for (int i = 0; i < bytes.Length; i++) adverse[i] = Convert.ToByte(byte.MaxValue - bytes[i]);
  35. return adverse;
  36. }
  37. /// <summary>克隆字节数组。当源为 NULL 时,将获取零元素字节数组。</summary>
  38. public static byte[] Clone(byte[] bytes)
  39. {
  40. if (bytes == null || bytes.LongLength < 0L) return Constant.EmptyBytes;
  41. var result = new byte[bytes.LongLength];
  42. bytes.CopyTo(result, 0L);
  43. return result;
  44. }
  45. /// <summary>为文本数据添加 BOM 字节,若已存在则忽略。</summary>
  46. public static byte[] AddTextBom(params byte[] text)
  47. {
  48. var bom = new byte[] { 0xEF, 0xBB, 0xBF };
  49. if (text == null || text.LongLength < 1L) return bom;
  50. if (text.LongLength >= 3L && text[0] == 0xEF && text[1] == 0xBB && text[2] == 0xBF)
  51. {
  52. // contains bom
  53. var array = new byte[text.LongLength];
  54. Array.Copy(text, 0L, array, 0L, text.LongLength);
  55. return array;
  56. }
  57. else
  58. {
  59. // add bom
  60. var array = new byte[text.LongLength + 3L];
  61. Array.Copy(bom, 0L, array, 0L, 3L);
  62. Array.Copy(text, 0L, array, 3L, text.LongLength);
  63. return array;
  64. }
  65. }
  66. /// <summary>去除文本数据的 BOM 字节,若不存在则忽略。</summary>
  67. public static byte[] WipeTextBom(byte[] bytes)
  68. {
  69. if (bytes == null) return Constant.EmptyBytes;
  70. var length = bytes.Length;
  71. if (length >= 3)
  72. {
  73. if ((bytes[0] == 0xEF) && (bytes[1] == 0xBB) && (bytes[2] == 0xBF))
  74. {
  75. var memory = new MemoryStream();
  76. memory.Write(bytes, 3, length - 3);
  77. var result = memory.ToArray();
  78. memory.Dispose();
  79. return result;
  80. }
  81. }
  82. return bytes;
  83. }
  84. /// <summary>确定此字节数组实例的开头是否与指定的字节数组匹配。</summary>
  85. public static bool StartsWith(byte[] bytes, params byte[] head)
  86. {
  87. var data = bytes;
  88. if (data == null) return false;
  89. if (head == null) return false;
  90. var datalength = data.LongLength;
  91. var headlength = head.LongLength;
  92. if (datalength < headlength) return false;
  93. if (headlength < 1L) return true;
  94. for (long i = 0; i < head.LongLength; i++)
  95. {
  96. if (data[i] != head[i]) return false;
  97. }
  98. return true;
  99. }
  100. /// <summary>确定此字节数组实例的结尾是否与指定的字节数组匹配。</summary>
  101. public static bool EndsWith(byte[] bytes, params byte[] head)
  102. {
  103. var data = bytes;
  104. if (data == null) return false;
  105. if (head == null) return false;
  106. var datalength = data.LongLength;
  107. var headlength = head.LongLength;
  108. if (datalength < headlength) return false;
  109. if (headlength < 1L) return true;
  110. for (long i = 0; i < headlength; i++)
  111. {
  112. var dataindex = datalength - i - 1;
  113. var headindex = headlength - i - 1;
  114. if (data[dataindex] != head[headindex]) return false;
  115. }
  116. return true;
  117. }
  118. #endregion
  119. #region Text
  120. /// <summary>将字节数组转换为十六进制文本。</summary>
  121. public static string ToHex(params byte[] bytes)
  122. {
  123. int length = bytes.Length;
  124. if (length > 0)
  125. {
  126. var sb = new StringBuilder();
  127. for (int i = 0; i < length; i++)
  128. {
  129. sb.Append(Constant.HexCollection[bytes[i] / 16]);
  130. sb.Append(Constant.HexCollection[bytes[i] % 16]);
  131. }
  132. return sb.ToString();
  133. }
  134. return "";
  135. }
  136. /// <summary>将十六进制文本转换为字节数组。</summary>
  137. public static byte[] FromHex(string hex)
  138. {
  139. if (string.IsNullOrEmpty(hex) || hex.Length < 2) return Constant.EmptyBytes;
  140. if (hex.Length % 2 != 0) return Constant.EmptyBytes;
  141. var lower = hex.ToLower().ToCharArray();
  142. var half = lower.Length / 2;
  143. var bytes = new byte[half];
  144. for (var i = 0; i < half; i++)
  145. {
  146. var offset = i * 2;
  147. var h = Constant.HexCollection.IndexOf(lower[offset]);
  148. var l = Constant.HexCollection.IndexOf(lower[offset + 1]);
  149. if (h < 0 || l < 0) return Constant.EmptyBytes;
  150. bytes[i] = Convert.ToByte((h * 16) + l);
  151. }
  152. return bytes;
  153. }
  154. /// <summary>将字节数组格式化为大写十六进制字符串。</summary>
  155. public static string ToX2(params byte[] bytes)
  156. {
  157. if (bytes == null) return "";
  158. var sb = new StringBuilder();
  159. for (int i = 0; i < bytes.Length; i++)
  160. {
  161. sb.Append(bytes[i].ToString("X2"));
  162. }
  163. return sb.ToString();
  164. }
  165. /// <summary>Byte[] -> Base64</summary>
  166. public static string ToBase64(params byte[] bytes)
  167. {
  168. if (bytes.Length < 1) return Constant.EmptyString;
  169. try { return Convert.ToBase64String(bytes); }
  170. catch { return Constant.EmptyString; }
  171. }
  172. /// <summary>Base64 -> Byte[]</summary>
  173. public static byte[] FromBase64(string base64)
  174. {
  175. if (string.IsNullOrEmpty(base64)) return Constant.EmptyBytes;
  176. try { return Convert.FromBase64String(base64); }
  177. catch { return Constant.EmptyBytes; }
  178. }
  179. /// <summary>转换字节数组为文本,默认使用 UTF-8 代码页。</summary>
  180. public static string ToText(byte[] bytes, Encoding encoding = null)
  181. {
  182. if (bytes.Length < 1) return Constant.EmptyString;
  183. try { return (encoding ?? Encoding.UTF8).GetString(bytes); }
  184. catch { return Constant.EmptyString; }
  185. }
  186. /// <summary>转换文本为字节数组,默认使用 UTF-8 代码页。</summary>
  187. public static byte[] FromText(string text, Encoding encoding = null)
  188. {
  189. if (string.IsNullOrEmpty(text)) return Constant.EmptyBytes;
  190. try { return (encoding ?? Encoding.UTF8).GetBytes(text); }
  191. catch { return Constant.EmptyBytes; }
  192. }
  193. /// <summary>检查字节数组包含 UTF-8 BOM 头。</summary>
  194. public static bool ContainsBOM(byte[] bytes)
  195. {
  196. if (bytes == null) return false;
  197. if (bytes.LongLength < 3L) return false;
  198. return bytes[0L] == 0xEF && bytes[1L] == 0xBB && bytes[2L] == 0xBF;
  199. }
  200. /// <summary>检查字节数组是 UTF-8 文本。可指定检查的最大字节长度。</summary>
  201. /// <param name="bytes">要检查的字节数组。</param>
  202. /// <param name="offset">已检查的偏移量。</param>
  203. /// <param name="checkLength">检查的最大字节长度。</param>
  204. public static bool IsUTF8(byte[] bytes, Class<int> offset, int checkLength = 1048576)
  205. {
  206. return IsUTF8(bytes, offset, checkLength);
  207. }
  208. /// <summary>检查字节数组是 UTF-8 文本,默认最多检测 1MB 数据。</summary>
  209. /// <param name="bytes">要检查的字节数组。</param>
  210. /// <param name="checkLength">检查的最大字节长度,指定为 0 将不限制检查长度。</param>
  211. /// <param name="offset">已检查的偏移量,用于调试。</param>
  212. public static bool IsUTF8(byte[] bytes, int checkLength = 1048576, Class<int> offset = null)
  213. {
  214. // UTF8在Unicode的基础上制定了这样一套规则:
  215. // 1.对于单字节字符,比特位的最高位为0;
  216. // 2.对于多字节字符,第一个字节的比特位中,最高位有n个1,剩下的n - 1个字节的比特位中,最高位都是10。
  217. // 好了,我知道你一定看不懂,那就先来看看下面例子后,再去看上面定义吧。
  218. // 比如一个字符(“A”),它在UTF8中的编码为(用二进制表示):01000001。由于比特位的最高位是0,表示它是单字节,它只需要1个字节就可以表示。
  219. // 再比如一个字符(“判”),它在UTF8中的编码为(用二进制表示):11100101 10001000 10100100。由于在第一个字节中,比特位最高位有3个1,说明这个字符总共需要3个字节来表示,且后3 - 1 = 2位字节中,比特位的最高位为10。
  220. if (bytes == null) return false;
  221. var length = bytes.LongLength;
  222. // 检查 BOM 头。
  223. if (ContainsBOM(bytes)) return true;
  224. var hasOffset = offset != null;
  225. var append = 0;
  226. if (hasOffset) offset.Value = 0;
  227. for (int i = 0; i < length; i++)
  228. {
  229. if (checkLength > 0 && i >= checkLength) break;
  230. var b = bytes[i];
  231. if (hasOffset) offset.Value = i;
  232. // 追加字节最高位为 0。
  233. if (append > 0)
  234. {
  235. if (b >> 6 != 2) return false;
  236. append -= 1;
  237. continue;
  238. }
  239. // ASCII 字符。
  240. if (b < 128) continue;
  241. // 2 字节 UTF-8。
  242. if (b >= 0xC0 && b <= 0xDF)
  243. {
  244. append = 1;
  245. continue;
  246. }
  247. // 3 字节 UTF-8 字符。
  248. if (b >= 0xE0 && b <= 0xEF)
  249. {
  250. append = 2;
  251. continue;
  252. }
  253. // 4 字节 UTF-8 字符。
  254. if (b >= 0xF0 && b <= 0xF7)
  255. {
  256. append = 3;
  257. continue;
  258. }
  259. // 5 字节 UTF-8 字符。
  260. if (b >= 0xF8 && b <= 0xFB)
  261. {
  262. append = 4;
  263. continue;
  264. }
  265. // 6 字节 UTF-8 字符。
  266. if (b >= 0xFC && b <= 0xFD)
  267. {
  268. append = 5;
  269. continue;
  270. }
  271. // 未知字节,非 UTF-8 定义。
  272. return false;
  273. }
  274. return true;
  275. }
  276. #endregion
  277. /// <summary>为字节数组增加字节。</summary>
  278. public static byte[] Append(byte[] head, params byte[] bytes)
  279. {
  280. var memory = new MemoryStream();
  281. if (head != null && head.Length > 0) Write(memory, head);
  282. if (bytes != null && bytes.Length > 0) Write(memory, bytes);
  283. var whole = memory.ToArray();
  284. return whole;
  285. }
  286. /// <summary>合并字节数组。</summary>
  287. public static byte[] Merge(params byte[][] array)
  288. {
  289. var memory = new MemoryStream();
  290. foreach (var bytes in array)
  291. {
  292. if (bytes == null || bytes.LongLength < 1L) continue;
  293. Write(memory, bytes);
  294. }
  295. var whole = memory.ToArray();
  296. return whole;
  297. }
  298. #region 压缩、解压。
  299. /// <summary>对数据进行 GZip 压缩。</summary>
  300. public static byte[] ToGzip(byte[] plain)
  301. {
  302. if (plain == null) return Constant.EmptyBytes;
  303. if (plain.Length == 0) return Constant.EmptyBytes;
  304. byte[] result;
  305. var output = new MemoryStream();
  306. var zip = new GZipStream(output, CompressionMode.Compress, true);
  307. zip.Write(plain, 0, plain.Length);
  308. zip.Close();
  309. zip.Dispose();
  310. result = output.ToArray();
  311. output.Close();
  312. output.Dispose();
  313. return result;
  314. }
  315. /// <summary>对数据进行 GZip 解压。</summary>
  316. public static byte[] FromGzip(byte[] gzip)
  317. {
  318. if (gzip == null) return Constant.EmptyBytes;
  319. if (gzip.Length == 0) return Constant.EmptyBytes;
  320. byte[] result;
  321. var input = new MemoryStream(gzip);
  322. var output = new MemoryStream();
  323. input.Position = 0;
  324. var zip = new GZipStream(input, CompressionMode.Decompress, true);
  325. Read(zip, output, 64, null);
  326. result = output.ToArray();
  327. zip.Close();
  328. zip.Dispose();
  329. input.Close();
  330. input.Dispose();
  331. output.Close();
  332. output.Dispose();
  333. return result;
  334. }
  335. /// <summary>压缩字典为 ZIP 文件。</summary>
  336. /// <param name="files">由文件名和文件内容组成的字典。</param>
  337. /// <param name="target">要输出的 ZIP 流。</param>
  338. public static Exception ToZip(Dictionary<string, byte[]> files, Stream target)
  339. {
  340. var zip = null as ZipOutputStream;
  341. try
  342. {
  343. if (files == null) return new ArgumentNullException();
  344. if (target == null) return new ArgumentNullException();
  345. if (!target.CanWrite) return new NotSupportedException();
  346. zip = new ZipOutputStream(target);
  347. zip.SetLevel(1);
  348. foreach (var file in files)
  349. {
  350. var crc = new Crc32();
  351. crc.Reset();
  352. crc.Update(file.Value);
  353. var zipentry = new ZipEntry(file.Key);
  354. zipentry.CompressionMethod = CompressionMethod.Deflated;
  355. //vzipentry.Size = vfile.Value.LongLength;
  356. zipentry.Crc = crc.Value;
  357. zipentry.IsUnicodeText = true;
  358. zip.PutNextEntry(zipentry);
  359. zip.Write(file.Value, 0, file.Value.Length);
  360. zip.CloseEntry();
  361. }
  362. zip.IsStreamOwner = false;
  363. zip.Finish();
  364. zip.Flush();
  365. zip.Close();
  366. return null;
  367. }
  368. catch (Exception ex)
  369. {
  370. KernelUtility.Dispose(zip);
  371. return ex;
  372. }
  373. }
  374. /// <summary>压缩到 ZIP 流。</summary>
  375. /// <param name="names">ZIP 内的文件名。</param>
  376. /// <param name="target">要输出的 ZIP 流。</param>
  377. /// <param name="modifildGetter">获取文件的修改时间。</param>
  378. /// <param name="disposeFiles">释放已写入 ZIP 的输入流。</param>
  379. public static Exception ToZip(IEnumerable<string> names, Stream target, Func<string, Stream> inputGetter, Func<string, DateTime> modifildGetter = null, bool disposeFiles = false)
  380. {
  381. var zip = null as ZipOutputStream;
  382. try
  383. {
  384. if (names == null) return new ArgumentNullException("names");
  385. if (target == null) return new ArgumentNullException("target");
  386. if (!target.CanWrite) return new ArgumentNullException("target");
  387. zip = new ZipOutputStream(target);
  388. zip.SetLevel(1);
  389. foreach (var name in names)
  390. {
  391. if (string.IsNullOrEmpty(name)) continue;
  392. // stream
  393. var capacity = 1024;
  394. var input = inputGetter == null ? null : inputGetter(name);
  395. if (input != null)
  396. {
  397. if (!input.CanSeek || !input.CanRead)
  398. {
  399. if (disposeFiles) KernelUtility.Dispose(input);
  400. return new NotSupportedException("获取到的输入流不支持 Seek 或 Read。");
  401. }
  402. }
  403. var crc = new Crc32();
  404. crc.Reset();
  405. if (input != null)
  406. {
  407. input.ResetPosition();
  408. while (true)
  409. {
  410. var count = 0;
  411. var buffer = new byte[capacity];
  412. count = input.Read(buffer, 0, buffer.Length);
  413. if (count == 0) break;
  414. crc.Update(buffer, 0, count);
  415. }
  416. }
  417. var entry = new ZipEntry(name);
  418. entry.CompressionMethod = CompressionMethod.Deflated;
  419. //vzipentry.Size = vfile.Value.LongLength;
  420. entry.Crc = crc.Value;
  421. entry.IsUnicodeText = true;
  422. if (modifildGetter != null) entry.DateTime = modifildGetter(name);
  423. zip.PutNextEntry(entry);
  424. if (input != null)
  425. {
  426. input.ResetPosition();
  427. while (true)
  428. {
  429. var count = 0;
  430. var buffer = new byte[capacity];
  431. count = input.Read(buffer, 0, buffer.Length);
  432. if (count == 0) break;
  433. zip.Write(buffer, 0, count);
  434. }
  435. }
  436. zip.CloseEntry();
  437. if (disposeFiles) KernelUtility.Dispose(input);
  438. }
  439. zip.IsStreamOwner = false;
  440. zip.Finish();
  441. zip.Flush();
  442. zip.Close();
  443. return null;
  444. }
  445. catch (Exception ex)
  446. {
  447. KernelUtility.Dispose(zip);
  448. return ex;
  449. }
  450. }
  451. /// <summary>压缩字典为 ZIP 包。</summary>
  452. /// <param name="files">由文件名和文件内容组成的字典。</param>
  453. public static byte[] ToZip(Dictionary<string, byte[]> files)
  454. {
  455. if (files == null) return null;
  456. var output = new MemoryStream();
  457. var input = null as Stream;
  458. var ex = ToZip(files.Keys, output, (name) =>
  459. {
  460. KernelUtility.Dispose(input);
  461. if (name.IsEmpty()) return null;
  462. var bytes = files[name];
  463. if (bytes == null || bytes.LongLength < 1L) return null;
  464. input = new MemoryStream();
  465. Write(input, bytes);
  466. return input;
  467. }, null, true);
  468. KernelUtility.Dispose(input);
  469. var result = output.ToArray();
  470. KernelUtility.Dispose(result);
  471. return result;
  472. }
  473. /// <summary>解压 ZIP 文件。</summary>
  474. public static Exception FromZip(Stream input, ZipOnFile onFile, ZipOnDirectory onDirectory = null, bool disposeOutput = false)
  475. {
  476. const int BufferCapacity = 1024;
  477. var zip = null as ZipInputStream;
  478. try
  479. {
  480. if (input == null) return new ArgumentNullException("input");
  481. if (!input.CanRead) return new NotSupportedException();
  482. if (onFile == null) return new ArgumentNullException("extraction");
  483. zip = new ZipInputStream(input);
  484. while (true)
  485. {
  486. var entry = zip.GetNextEntry();
  487. if (entry == null) break;
  488. var name = entry.Name;
  489. var size = entry.Size;
  490. var modified = entry.DateTime;
  491. if (entry.IsFile)
  492. {
  493. var output = null as Stream;
  494. try
  495. {
  496. output = onFile(name, size, modified);
  497. if (output == null) continue;
  498. if (!output.CanWrite)
  499. {
  500. KernelUtility.Dispose(output);
  501. continue;
  502. }
  503. var writed = 0L;
  504. while (true)
  505. {
  506. var buffer = new byte[BufferCapacity];
  507. var count = zip.Read(buffer, 0, BufferCapacity);
  508. writed += count;
  509. if (count < 1) break;
  510. output.Write(buffer, 0, count);
  511. }
  512. if (disposeOutput) KernelUtility.Dispose(output, true);
  513. }
  514. catch (Exception ex)
  515. {
  516. if (disposeOutput) KernelUtility.Dispose(output, true);
  517. KernelUtility.Dispose(zip);
  518. return ex;
  519. }
  520. }
  521. if (onDirectory != null && entry.IsDirectory)
  522. {
  523. try
  524. {
  525. onDirectory(name, modified);
  526. }
  527. catch (Exception ex)
  528. {
  529. KernelUtility.Dispose(zip);
  530. return ex;
  531. }
  532. }
  533. }
  534. zip.Dispose();
  535. return null;
  536. }
  537. catch (Exception ex)
  538. {
  539. KernelUtility.Dispose(zip);
  540. return ex;
  541. }
  542. }
  543. /// <summary>解压 .ZIP 文件为字典。</summary>
  544. public static Dictionary<string, byte[]> FromZip(byte[] zip)
  545. {
  546. var result = new Dictionary<string, byte[]>();
  547. if (zip == null) return result;
  548. if (zip.LongLength < 1) return result;
  549. var packagememory = new System.IO.MemoryStream(zip);
  550. try
  551. {
  552. var zipstream = new ZipInputStream(packagememory);
  553. while (true)
  554. {
  555. var entry = zipstream.GetNextEntry();
  556. if (entry == null) break;
  557. if (entry.IsFile)
  558. {
  559. var cellname = entry.Name;
  560. var celldata = new byte[0];
  561. {
  562. var cellstream = new System.IO.MemoryStream();
  563. while (true)
  564. {
  565. var blockdata = new byte[1024];
  566. var blockread = zipstream.Read(blockdata, 0, 1024);
  567. if (blockread < 1) break;
  568. cellstream.Write(blockdata, 0, blockread);
  569. }
  570. celldata = cellstream.ToArray();
  571. cellstream.Dispose();
  572. }
  573. if (result.ContainsKey(cellname)) result[cellname] = celldata;
  574. else result.Add(cellname, celldata);
  575. }
  576. }
  577. zipstream.Dispose();
  578. }
  579. catch (Exception ex)
  580. {
  581. Debug.WriteLine(ex.ToString());
  582. }
  583. packagememory.Dispose();
  584. return result;
  585. }
  586. #endregion
  587. #region Stream
  588. /// <summary>关闭,并释放流。</summary>
  589. public static void Dispose(Stream stream, bool flush = false, bool close = true)
  590. {
  591. if (stream != null)
  592. {
  593. try { if (flush) stream.Flush(); } catch { }
  594. try { if (close) stream.Close(); } catch { }
  595. try { stream.Dispose(); } catch { }
  596. }
  597. }
  598. /// <summary>关闭,并释放流。</summary>
  599. public static void Dispose(IEnumerable<Stream> streams, bool flush = false, bool close = true)
  600. {
  601. if (streams != null)
  602. {
  603. foreach (var stream in streams) Dispose(stream, flush, close);
  604. }
  605. }
  606. /// <summary>重置流的位置到开始位置。</summary>
  607. public static bool ResetPosition(Stream stream)
  608. {
  609. if (stream == null) return false;
  610. try
  611. {
  612. stream.Position = 0;
  613. if (stream.CanSeek) stream.Seek(0, SeekOrigin.Begin);
  614. return true;
  615. }
  616. catch
  617. {
  618. return false;
  619. }
  620. }
  621. /// <summary></summary>
  622. private static long Read(Stream source, Stream destination, int capacit, Action<Int64> progress)
  623. {
  624. if (source == null) return 0;
  625. if (destination == null) return 0;
  626. if (!source.CanRead) return 0;
  627. if (!destination.CanWrite) return 0;
  628. long result = 0;
  629. var failed = false;
  630. var callback = progress != null;
  631. while (true)
  632. {
  633. var count = 0;
  634. var buffer = new byte[capacit];
  635. try { count = source.Read(buffer, 0, buffer.Length); } catch { failed = true; }
  636. if (failed) break;
  637. if (callback) progress(result);
  638. if (count == 0) break;
  639. try { destination.Write(buffer, 0, count); } catch { failed = true; }
  640. if (failed) break;
  641. result += count;
  642. }
  643. return result;
  644. }
  645. /// <summary>读取源流中的数据,并将数据写入目标流,获取写入的字节数。</summary>
  646. /// <param name="source">源流。</param>
  647. /// <param name="destination">目标流。</param>
  648. /// <param name="progress">进度回调。</param>
  649. public static long Read(Stream source, Stream destination, Action<Int64> progress = null)
  650. {
  651. return Read(source, destination, Constant.DefaultBufferCapacity, progress);
  652. }
  653. /// <summary>读取源流中的数据,并将数据写入目标流,获取写入的总字节数。</summary>
  654. /// <param name="sources">源流。</param>
  655. /// <param name="destination">目标流。</param>
  656. public static long Read(IEnumerable<Stream> sources, Stream destination)
  657. {
  658. var count = 0L;
  659. if (sources != null)
  660. {
  661. foreach (var source in sources)
  662. {
  663. count += Read(source, destination, Constant.DefaultBufferCapacity, null);
  664. }
  665. }
  666. return count;
  667. }
  668. /// <summary>读取源流中的数据。</summary>
  669. /// <param name="source">源流。</param>
  670. /// <param name="dispose">读取结束后释放源流。</param>
  671. public static byte[] Read(Stream source, bool dispose = false)
  672. {
  673. var memory = new MemoryStream();
  674. Read(source, memory, Constant.DefaultBufferCapacity, null);
  675. var result = memory.ToArray();
  676. Dispose(memory);
  677. if (dispose) Dispose(source);
  678. return result;
  679. }
  680. /// <summary>向目标流写入数据。</summary>
  681. /// <param name="destination">目标流。</param>
  682. /// <param name="bytes">要写入的数据。</param>
  683. public static long Write(Stream destination, params byte[] bytes)
  684. {
  685. if (bytes == null) return 0L;
  686. if (bytes.LongLength < 1L) return 0L;
  687. if (destination == null) return 0L;
  688. var count = 0L;
  689. try
  690. {
  691. if (!destination.CanWrite) return 0L;
  692. for (var i = 0L; i < bytes.LongLength; i++)
  693. {
  694. destination.WriteByte(bytes[i]);
  695. count += 1L;
  696. }
  697. }
  698. catch { }
  699. return count;
  700. }
  701. #endregion
  702. #region AES
  703. private static RijndaelManaged Aes256Provider(byte[] argKey)
  704. {
  705. var key = argKey ?? Constant.EmptyBytes; // AesFill(argKey);
  706. var p = new RijndaelManaged();
  707. p.Key = key;
  708. p.Mode = CipherMode.ECB;
  709. p.Padding = PaddingMode.PKCS7;
  710. return p;
  711. }
  712. /// <summary>对数据进行 AES 加密。</summary>
  713. public static byte[] Aes256Encrypt(byte[] bytes, byte[] key = null)
  714. {
  715. if (bytes == null) return Constant.EmptyBytes;
  716. if (bytes.Length == 0) return Constant.EmptyBytes;
  717. var rm = Aes256Provider(key);
  718. var result = new byte[0];
  719. var ct = rm.CreateEncryptor();
  720. try
  721. {
  722. result = ct.TransformFinalBlock(bytes, 0, bytes.Length);
  723. }
  724. catch { }
  725. return result;
  726. }
  727. /// <summary>对数据进行 AES 解密。</summary>
  728. public static byte[] Aes256Decrypt(byte[] cipher, byte[] key = null)
  729. {
  730. if (cipher == null) return Constant.EmptyBytes;
  731. if (cipher.Length == 0) return Constant.EmptyBytes;
  732. var rm = Aes256Provider(key);
  733. var result = new byte[0];
  734. var ct = rm.CreateDecryptor();
  735. try
  736. {
  737. result = ct.TransformFinalBlock(cipher, 0, cipher.Length);
  738. }
  739. catch { }
  740. ct.Dispose();
  741. return result;
  742. }
  743. #endregion
  744. #region Hash
  745. private static byte[] ComputeHash(HashAlgorithm csp, byte[] bytes)
  746. {
  747. if (bytes != null)
  748. {
  749. try
  750. {
  751. var result = csp.ComputeHash(bytes);
  752. csp.Clear();
  753. #if !NET20
  754. csp.Dispose();
  755. #endif
  756. return result;
  757. }
  758. catch { }
  759. }
  760. return Constant.EmptyBytes;
  761. }
  762. private static byte[] ComputeHash(HashAlgorithm csp, Stream stream, bool dispose, Action<Int64> progress)
  763. {
  764. if (progress == null)
  765. {
  766. if (stream != null)
  767. {
  768. var result = Constant.EmptyBytes;
  769. try
  770. {
  771. result = csp.ComputeHash(stream);
  772. csp.Clear();
  773. #if !NET20
  774. csp.Dispose();
  775. #endif
  776. }
  777. catch { }
  778. if (dispose) stream.Dispose();
  779. return result;
  780. }
  781. return Constant.EmptyBytes;
  782. }
  783. else
  784. {
  785. if (stream == null) return Constant.EmptyBytes;
  786. // 初始化。
  787. var validcallback = progress != null;
  788. var capacity = Constant.DefaultBufferCapacity;
  789. var buffer = new byte[capacity];
  790. csp.Initialize();
  791. // 读取。
  792. var failed = false;
  793. while (true)
  794. {
  795. var read = 0;
  796. try { read = stream.Read(buffer, 0, capacity); }
  797. catch { failed = true; }
  798. if (read < capacity)
  799. {
  800. if (read < 1)
  801. {
  802. csp.TransformFinalBlock(new byte[0], 0, 0);
  803. }
  804. else
  805. {
  806. csp.TransformFinalBlock(buffer, 0, Convert.ToInt32(read));
  807. }
  808. break;
  809. }
  810. else
  811. {
  812. csp.TransformBlock(buffer, 0, Convert.ToInt32(read), buffer, 0);
  813. }
  814. }
  815. if (failed)
  816. {
  817. csp.Clear();
  818. #if !NET20
  819. csp.Dispose();
  820. #endif
  821. return Constant.EmptyBytes;
  822. }
  823. else
  824. {
  825. var result = csp.Hash;
  826. csp.Clear();
  827. #if !NET20
  828. csp.Dispose();
  829. #endif
  830. return result;
  831. }
  832. }
  833. }
  834. /// <summary>获取 MD5 值。</summary>
  835. public static byte[] MD5(params byte[] bytes)
  836. {
  837. return ComputeHash(new MD5CryptoServiceProvider(), bytes);
  838. }
  839. /// <summary>获取 MD5 值。</summary>
  840. public static byte[] MD5(Stream stream, Action<Int64> progress = null)
  841. {
  842. return ComputeHash(new MD5CryptoServiceProvider(), stream, false, progress);
  843. }
  844. /// <summary>获取 SHA1 值。</summary>
  845. public static byte[] SHA1(params byte[] bytes)
  846. {
  847. return ComputeHash(new SHA1CryptoServiceProvider(), bytes);
  848. }
  849. /// <summary>获取 SHA1 值。</summary>
  850. public static byte[] SHA1(Stream stream, Action<Int64> progress = null)
  851. {
  852. return ComputeHash(new SHA1CryptoServiceProvider(), stream, false, progress);
  853. }
  854. #if !NET20
  855. /// <summary>获取 SHA256 值。</summary>
  856. public static byte[] SHA256(params byte[] bytes)
  857. {
  858. return ComputeHash(new SHA256CryptoServiceProvider(), bytes);
  859. }
  860. /// <summary>获取 SHA256 值。</summary>
  861. public static byte[] SHA256(Stream stream, Action<Int64> progress = null)
  862. {
  863. return ComputeHash(new SHA256CryptoServiceProvider(), stream, false, progress);
  864. }
  865. #endif
  866. #endregion
  867. }
  868. }