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.

951 lines
35 KiB

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