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.

984 lines
36 KiB

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