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.

949 lines
35 KiB

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