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.

1127 lines
43 KiB

2 years ago
5 years ago
4 years ago
4 years ago
4 years ago
2 years ago
2 years ago
2 years ago
5 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
5 years ago
2 years ago
2 years ago
5 years ago
2 years ago
2 years ago
5 years ago
5 years ago
2 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
2 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
3 years ago
5 years ago
5 years ago
5 years ago
2 years ago
5 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
5 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 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 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)) return Empty;
  158. var chars = hex.ToLower().ToCharArray();
  159. chars = chars.FindAll(x => x != ' ' && x != '-');
  160. if (chars.Length < 1 || chars.Length % 2 != 0) return Empty;
  161. var half = chars.Length / 2;
  162. var bytes = new byte[half];
  163. for (var i = 0; i < half; i++)
  164. {
  165. var offset = i * 2;
  166. var h = Constant.HexCollection.IndexOf(chars[offset]);
  167. var l = Constant.HexCollection.IndexOf(chars[offset + 1]);
  168. if (h < 0 || l < 0) return Empty;
  169. bytes[i] = Convert.ToByte((h * 16) + l);
  170. }
  171. return bytes;
  172. }
  173. /// <summary>将字节数组格式化为十六进制字符串,可指定大小写。</summary>
  174. /// <remarks>例:D41D8CD98F00B204E9800998ECF8427E</remarks>
  175. public static string ToX2(this byte[] bytes, bool upper = true)
  176. {
  177. if (bytes == null) return "";
  178. var length = bytes.Length;
  179. if (length < 1) return TextUtility.Empty;
  180. var hex = upper ? UpperHex : LowerHex;
  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>将字节数组格式化为十六进制字符串,可指定大小写。</summary>
  192. /// <remarks>例:D41D8CD98F00B204E9800998ECF8427E</remarks>
  193. public static string ToX2(this byte[] bytes, string separator, bool upper = true)
  194. {
  195. if (bytes == null) return "";
  196. var join = separator.NotEmpty();
  197. var length = bytes.Length;
  198. if (length < 1) return TextUtility.Empty;
  199. var sb = new StringBuilder();
  200. var hex = upper ? UpperHex : LowerHex;
  201. for (var i = 0; i < length; i++)
  202. {
  203. var value = bytes[i];
  204. var offset = i * 2;
  205. var a = hex[value / 16];
  206. var b = hex[value % 16];
  207. if (i > 0 && join) sb.Append(separator);
  208. sb.Append(a);
  209. sb.Append(b);
  210. }
  211. return sb.ToString();
  212. }
  213. /// <summary>Byte[] -> Base64</summary>
  214. public static string ToBase64(params byte[] bytes)
  215. {
  216. if (bytes == null || bytes.Length < 1) return Constant.EmptyString;
  217. try { return Convert.ToBase64String(bytes); }
  218. catch { return Constant.EmptyString; }
  219. }
  220. /// <summary>Base64 -> Byte[]</summary>
  221. public static byte[] FromBase64(string base64)
  222. {
  223. if (string.IsNullOrEmpty(base64)) return Empty;
  224. try { return Convert.FromBase64String(base64); }
  225. catch { return Empty; }
  226. }
  227. /// <summary>转换字节数组为文本,默认使用 UTF-8 代码页。</summary>
  228. public static string ToText(byte[] bytes, Encoding encoding = null)
  229. {
  230. if (bytes.Length < 1) return Constant.EmptyString;
  231. try { return (encoding ?? Encoding.UTF8).GetString(bytes); }
  232. catch { return Constant.EmptyString; }
  233. }
  234. /// <summary>转换文本为字节数组,默认使用 UTF-8 代码页。</summary>
  235. public static byte[] FromText(string text, Encoding encoding = null)
  236. {
  237. if (string.IsNullOrEmpty(text)) return Empty;
  238. try { return (encoding ?? Encoding.UTF8).GetBytes(text); }
  239. catch { return Empty; }
  240. }
  241. #endregion
  242. #region 压缩、解压。
  243. /// <summary>对数据进行 GZip 压缩。</summary>
  244. public static byte[] ToGzip(byte[] plain)
  245. {
  246. if (plain == null || plain.Length == 0) return Empty;
  247. byte[] result;
  248. using (var output = new MemoryStream())
  249. {
  250. using (var zip = new GZipStream(output, CompressionMode.Compress, true))
  251. {
  252. zip.Write(plain, 0, plain.Length);
  253. }
  254. result = output.ToArray();
  255. }
  256. return result;
  257. }
  258. /// <summary>对数据进行 GZip 解压。</summary>
  259. public static byte[] FromGzip(byte[] gzip)
  260. {
  261. if (gzip == null || gzip.Length == 0) return Empty;
  262. byte[] result;
  263. using (var input = new MemoryStream(gzip))
  264. {
  265. input.Position = 0;
  266. using (var output = new MemoryStream())
  267. {
  268. using (var zip = new GZipStream(input, CompressionMode.Decompress, true))
  269. {
  270. Read(zip, output, null, 8192);
  271. result = output.ToArray();
  272. }
  273. }
  274. }
  275. return result;
  276. }
  277. /// <summary>压缩字典为 ZIP 文件。</summary>
  278. /// <param name="files">由文件名和文件内容组成的字典。</param>
  279. /// <param name="target">要输出的 ZIP 流。</param>
  280. public static Exception ToZip(Dictionary<string, byte[]> files, Stream target)
  281. {
  282. var zip = null as ZipOutputStream;
  283. try
  284. {
  285. if (files == null) return new ArgumentNullException();
  286. if (target == null) return new ArgumentNullException();
  287. if (!target.CanWrite) return new NotSupportedException();
  288. zip = new ZipOutputStream(target);
  289. zip.SetLevel(1);
  290. foreach (var file in files)
  291. {
  292. var crc = new Crc32();
  293. crc.Reset();
  294. crc.Update(file.Value);
  295. var zipentry = new ZipEntry(file.Key);
  296. zipentry.CompressionMethod = CompressionMethod.Deflated;
  297. //vzipentry.Size = vfile.Value.LongLength;
  298. zipentry.Crc = crc.Value;
  299. zipentry.IsUnicodeText = true;
  300. zip.PutNextEntry(zipentry);
  301. zip.Write(file.Value, 0, file.Value.Length);
  302. zip.CloseEntry();
  303. }
  304. zip.IsStreamOwner = false;
  305. zip.Finish();
  306. zip.Flush();
  307. zip.Close();
  308. return null;
  309. }
  310. catch (Exception ex)
  311. {
  312. RuntimeUtility.Dispose(zip);
  313. return ex;
  314. }
  315. }
  316. /// <summary>压缩到 ZIP 流。</summary>
  317. /// <param name="names">ZIP 内的文件名。</param>
  318. /// <param name="target">要输出的 ZIP 流。</param>
  319. /// <param name="inputGetter">按名称获取文件的输入流。</param>
  320. /// <param name="modifildGetter">按名称获取文件的修改时间。</param>
  321. /// <param name="disposeFiles">释放已写入 ZIP 的输入流。</param>
  322. public static Exception ToZip(IEnumerable<string> names, Stream target, Func<string, Stream> inputGetter, Func<string, DateTime> modifildGetter = null, bool disposeFiles = false)
  323. {
  324. var zip = null as ZipOutputStream;
  325. try
  326. {
  327. if (names == null) return new ArgumentNullException("names");
  328. if (target == null) return new ArgumentNullException("target");
  329. if (!target.CanWrite) return new ArgumentNullException("target");
  330. zip = new ZipOutputStream(target);
  331. zip.SetLevel(1);
  332. foreach (var name in names)
  333. {
  334. if (string.IsNullOrEmpty(name)) continue;
  335. // stream
  336. var capacity = 1024;
  337. var input = inputGetter == null ? null : inputGetter(name);
  338. if (input != null)
  339. {
  340. if (!input.CanSeek || !input.CanRead)
  341. {
  342. if (disposeFiles) RuntimeUtility.Dispose(input);
  343. return new NotSupportedException("获取到的输入流不支持 Seek 或 Read。");
  344. }
  345. }
  346. var crc = new Crc32();
  347. crc.Reset();
  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. crc.Update(buffer, 0, count);
  358. }
  359. }
  360. var entry = new ZipEntry(name);
  361. entry.CompressionMethod = CompressionMethod.Deflated;
  362. //vzipentry.Size = vfile.Value.LongLength;
  363. entry.Crc = crc.Value;
  364. entry.IsUnicodeText = true;
  365. if (modifildGetter != null) entry.DateTime = modifildGetter(name);
  366. zip.PutNextEntry(entry);
  367. if (input != null)
  368. {
  369. input.ResetPosition();
  370. while (true)
  371. {
  372. var count = 0;
  373. var buffer = new byte[capacity];
  374. count = input.Read(buffer, 0, buffer.Length);
  375. if (count == 0) break;
  376. zip.Write(buffer, 0, count);
  377. }
  378. }
  379. zip.CloseEntry();
  380. if (disposeFiles) RuntimeUtility.Dispose(input);
  381. }
  382. zip.IsStreamOwner = false;
  383. zip.Finish();
  384. zip.Flush();
  385. zip.Close();
  386. return null;
  387. }
  388. catch (Exception ex)
  389. {
  390. RuntimeUtility.Dispose(zip);
  391. return ex;
  392. }
  393. }
  394. /// <summary>压缩字典为 ZIP 包。</summary>
  395. /// <param name="files">由文件名和文件内容组成的字典。</param>
  396. public static byte[] ToZip(Dictionary<string, byte[]> files)
  397. {
  398. if (files == null) return null;
  399. var output = new MemoryStream();
  400. var input = null as Stream;
  401. var ex = ToZip(files.Keys, output, (name) =>
  402. {
  403. RuntimeUtility.Dispose(input);
  404. if (name.IsEmpty()) return null;
  405. var bytes = files[name];
  406. if (bytes == null || bytes.LongLength < 1L) return null;
  407. input = new MemoryStream();
  408. Write(input, bytes);
  409. return input;
  410. }, null, true);
  411. RuntimeUtility.Dispose(input);
  412. var result = output.ToArray();
  413. RuntimeUtility.Dispose(result);
  414. return result;
  415. }
  416. /// <summary>解压 ZIP 文件。</summary>
  417. public static Exception FromZip(Stream input, ZipOnFile onFile, ZipOnDirectory onDirectory = null, bool disposeOutput = false)
  418. {
  419. const int BufferCapacity = 1024;
  420. var zip = null as ZipInputStream;
  421. try
  422. {
  423. if (input == null) return new ArgumentNullException("input");
  424. if (!input.CanRead) return new NotSupportedException();
  425. if (onFile == null) return new ArgumentNullException("extraction");
  426. zip = new ZipInputStream(input);
  427. while (true)
  428. {
  429. var entry = zip.GetNextEntry();
  430. if (entry == null) break;
  431. var name = entry.Name;
  432. var size = entry.Size;
  433. var modified = entry.DateTime;
  434. if (entry.IsFile)
  435. {
  436. var output = null as Stream;
  437. try
  438. {
  439. output = onFile(name, size, modified);
  440. if (output == null) continue;
  441. if (!output.CanWrite)
  442. {
  443. RuntimeUtility.Dispose(output);
  444. continue;
  445. }
  446. var writed = 0L;
  447. while (true)
  448. {
  449. var buffer = new byte[BufferCapacity];
  450. var count = zip.Read(buffer, 0, BufferCapacity);
  451. writed += count;
  452. if (count < 1) break;
  453. output.Write(buffer, 0, count);
  454. }
  455. if (disposeOutput) RuntimeUtility.Dispose(output, true);
  456. }
  457. catch (Exception ex)
  458. {
  459. if (disposeOutput) RuntimeUtility.Dispose(output, true);
  460. RuntimeUtility.Dispose(zip);
  461. return ex;
  462. }
  463. }
  464. if (onDirectory != null && entry.IsDirectory)
  465. {
  466. try
  467. {
  468. onDirectory(name, modified);
  469. }
  470. catch (Exception ex)
  471. {
  472. RuntimeUtility.Dispose(zip);
  473. return ex;
  474. }
  475. }
  476. }
  477. zip.Dispose();
  478. return null;
  479. }
  480. catch (Exception ex)
  481. {
  482. RuntimeUtility.Dispose(zip);
  483. return ex;
  484. }
  485. }
  486. /// <summary>解压 .ZIP 文件为字典。</summary>
  487. public static Dictionary<string, byte[]> FromZip(byte[] zip)
  488. {
  489. var result = new Dictionary<string, byte[]>();
  490. if (zip == null) return result;
  491. if (zip.LongLength < 1) return result;
  492. var packagememory = new System.IO.MemoryStream(zip);
  493. try
  494. {
  495. var zipstream = new ZipInputStream(packagememory);
  496. while (true)
  497. {
  498. var entry = zipstream.GetNextEntry();
  499. if (entry == null) break;
  500. if (entry.IsFile)
  501. {
  502. var cellname = entry.Name;
  503. var celldata = new byte[0];
  504. {
  505. var cellstream = new System.IO.MemoryStream();
  506. while (true)
  507. {
  508. var blockdata = new byte[1024];
  509. var blockread = zipstream.Read(blockdata, 0, 1024);
  510. if (blockread < 1) break;
  511. cellstream.Write(blockdata, 0, blockread);
  512. }
  513. celldata = cellstream.ToArray();
  514. cellstream.Dispose();
  515. }
  516. if (result.ContainsKey(cellname)) result[cellname] = celldata;
  517. else result.Add(cellname, celldata);
  518. }
  519. }
  520. zipstream.Dispose();
  521. }
  522. catch (Exception ex)
  523. {
  524. Debug.WriteLine(ex.ToString());
  525. }
  526. packagememory.Dispose();
  527. return result;
  528. }
  529. #endregion
  530. #region Stream
  531. /// <summary>关闭,并释放流。</summary>
  532. public static void Dispose(Stream stream, bool flush = false, bool close = true)
  533. {
  534. if (stream != null)
  535. {
  536. try { if (flush) stream.Flush(); } catch { }
  537. try { if (close) stream.Close(); } catch { }
  538. try { stream.Dispose(); } catch { }
  539. }
  540. }
  541. /// <summary>关闭,并释放流。</summary>
  542. public static void Dispose(IEnumerable<Stream> streams, bool flush = false, bool close = true)
  543. {
  544. if (streams != null)
  545. {
  546. foreach (var stream in streams) Dispose(stream, flush, close);
  547. }
  548. }
  549. /// <summary>重置流的位置到开始位置。</summary>
  550. public static bool ResetPosition(Stream stream)
  551. {
  552. if (stream == null) return false;
  553. try
  554. {
  555. stream.Position = 0;
  556. if (stream.CanSeek) stream.Seek(0, SeekOrigin.Begin);
  557. return true;
  558. }
  559. catch
  560. {
  561. return false;
  562. }
  563. }
  564. /// <summary>读取源流中的数据,并将数据写入目标流,获取写入的总字节数。</summary>
  565. /// <param name="source">要读取的源流。</param>
  566. /// <param name="destination">要写入的目标流。</param>
  567. /// <param name="progress">已写入的字节数,返回 TRUE 继续读取,返回 FALSE 中断读取。</param>
  568. /// <param name="buffer">缓冲区大小,最小值为 1。</param>
  569. /// <returns>已写入的字节数。</returns>
  570. public static long Read(Stream source, Stream destination, Func<long, bool> progress = null, int buffer = DefaultBuffer)
  571. {
  572. if (source == null || !source.CanRead) return 0;
  573. if (destination == null || !destination.CanWrite) return 0;
  574. var limit = buffer < 1 ? 1 : buffer;
  575. var total = 0L;
  576. var count = 0;
  577. var callback = progress == null ? false : true;
  578. while (true)
  579. {
  580. count = 0;
  581. var temp = new byte[limit];
  582. try
  583. {
  584. count = source.Read(temp, 0, limit);
  585. if (count < 1) break;
  586. }
  587. catch { break; }
  588. try
  589. {
  590. destination.Write(temp, 0, count);
  591. }
  592. catch { break; }
  593. total += count;
  594. if (callback)
  595. {
  596. var @continue = progress(total);
  597. if (!@continue) break;
  598. }
  599. }
  600. return total;
  601. }
  602. /// <summary>读取源流中的数据,并将数据写入目标流,获取写入的总字节数。</summary>
  603. /// <param name="source">要读取的源流。</param>
  604. /// <param name="destination">要写入的目标流。</param>
  605. /// <param name="progress">已写入的字节数,返回 TRUE 继续读取,返回 FALSE 中断读取。</param>
  606. /// <param name="buffer">缓冲区大小,最小值为 1。</param>
  607. /// <returns>已写入的字节数。</returns>
  608. public static long Read(Stream source, Stream destination, Action<long> progress, int buffer = DefaultBuffer)
  609. {
  610. return Read(source, destination, (x) => { progress?.Invoke(x); return true; }, buffer);
  611. }
  612. /// <summary>读取流。</summary>
  613. /// <exception cref="ArgumentNullException" />
  614. public static int Read(this Stream stream, byte[] buffer)
  615. {
  616. if (stream == null) throw new ArgumentNullException(nameof(stream));
  617. if (buffer == null) throw new ArgumentNullException(nameof(buffer));
  618. return stream.Read(buffer, 0, buffer.Length);
  619. }
  620. /// <summary>读取源流中的数据。</summary>
  621. /// <param name="source">源流。</param>
  622. /// <param name="buffer">缓冲区大小,最小值为 1。</param>
  623. /// <param name="dispose">读取结束后释放源流。</param>
  624. public static byte[] Read(Stream source, int buffer = 4096, bool dispose = false)
  625. {
  626. var result = null as byte[];
  627. using (var memory = new MemoryStream())
  628. {
  629. Read(source, memory, null, buffer);
  630. result = memory.ToArray();
  631. }
  632. if (dispose) Dispose(source);
  633. return result;
  634. }
  635. /// <summary>读取源流中的数据。</summary>
  636. /// <param name="source">源流。</param>
  637. /// <param name="dispose">读取结束后释放源流。</param>
  638. public static byte[] Read(Stream source, bool dispose) => Read(source, DefaultBuffer, dispose);
  639. /// <summary>读取源流中的数据,并将数据写入目标流,获取写入的总字节数。</summary>
  640. /// <param name="sources">要读取的源流。</param>
  641. /// <param name="destination">要写入的目标流。</param>
  642. /// <param name="writed">已写入的字节数,返回 TRUE 继续读取,返回 FALSE 中断读取。</param>
  643. /// <param name="buffer">缓冲区大小,最小值为 1。</param>
  644. /// <returns>已写入的字节数。</returns>
  645. public static long Read(IEnumerable<Stream> sources, Stream destination, Func<long, bool> writed = null, int buffer = DefaultBuffer)
  646. {
  647. var total = 0L;
  648. if (sources != null)
  649. {
  650. if (writed == null)
  651. {
  652. foreach (var source in sources) total += Read(source, destination, null, buffer);
  653. }
  654. else
  655. {
  656. foreach (var source in sources)
  657. {
  658. Read(source, destination, (x) =>
  659. {
  660. total += x;
  661. return writed(total);
  662. }, buffer);
  663. }
  664. }
  665. }
  666. return total;
  667. }
  668. /// <summary>读取源流中的数据,并将数据写入目标流,获取写入的总字节数。</summary>
  669. /// <param name="sources">要读取的源流。</param>
  670. /// <param name="destination">要写入的目标流。</param>
  671. /// <param name="writed">已写入的字节数,返回 TRUE 继续读取,返回 FALSE 中断读取。</param>
  672. /// <param name="buffer">缓冲区大小,最小值为 1。</param>
  673. /// <returns>已写入的字节数。</returns>
  674. public static long Read(IEnumerable<Stream> sources, Stream destination, Action<long> writed, int buffer = DefaultBuffer)
  675. {
  676. return Read(sources, destination, (x) => { writed?.Invoke(x); return true; }, buffer);
  677. }
  678. /// <summary>向目标流写入数据,最多可写入 2147483648 字节。</summary>
  679. public static int Write(Stream destination, byte[] bytes, Action<long> writed, int buffer = DefaultBuffer)
  680. {
  681. if (destination == null || !destination.CanWrite) return 0;
  682. if (bytes == null || bytes.Length < 1 || bytes.LongLength > int.MaxValue) return 0;
  683. var limit = buffer < 1 ? 1 : buffer;
  684. var total = 0;
  685. try
  686. {
  687. var length = bytes.Length;
  688. while (total < length)
  689. {
  690. var block = length - total;
  691. if (block > limit) block = limit;
  692. destination.Write(bytes, total, block);
  693. total += block;
  694. writed?.Invoke(total);
  695. }
  696. }
  697. catch (Exception ex)
  698. {
  699. Logger.Internals.Exception(ex, $"{nameof(BytesUtility)}.{nameof(Write)}");
  700. }
  701. return total;
  702. }
  703. /// <summary>向目标流写入数据,最多可写入 2147483647 字节(> 20 GB)。</summary>
  704. public static int Write(Stream destination, params byte[] bytes) => Write(destination, bytes, null);
  705. #endregion
  706. #region PKCS #5
  707. /// <summary>使用密码生成密钥。</summary>
  708. static byte[] PKCS5(byte[] password, byte[] salt = null, int iterations = 1000, int bits = 32)
  709. {
  710. var rfc2898 = new Rfc2898DeriveBytes(password, salt, 1);
  711. return rfc2898.GetBytes(32);
  712. }
  713. #endregion
  714. #region AES
  715. private static void Aes256(byte[] key, byte[] salt, Func<RijndaelManaged, ICryptoTransform> create, Stream input, Stream output)
  716. {
  717. using (var rm = new RijndaelManaged())
  718. {
  719. rm.KeySize = 256;
  720. rm.BlockSize = 128;
  721. rm.Mode = CipherMode.ECB;
  722. rm.Padding = PaddingMode.PKCS7;
  723. var k = new Rfc2898DeriveBytes(SHA256(key), salt, 1);
  724. rm.Key = k.GetBytes(32);
  725. rm.IV = k.GetBytes(16);
  726. using (var ct = create.Invoke(rm))
  727. {
  728. using (var cs = new CryptoStream(output, ct, CryptoStreamMode.Write))
  729. {
  730. Read(input, cs);
  731. cs.Close();
  732. }
  733. }
  734. }
  735. }
  736. /// <summary>执行 AES 加密。</summary>
  737. public static void Aes256Encrypt(Stream input, Stream output, byte[] key) => Aes256(key, key, rm => rm.CreateEncryptor(), input, output);
  738. /// <summary>执行 AES 解密。</summary>
  739. public static void Aes256Decrypt(Stream input, Stream output, byte[] key) => Aes256(key, key, rm => rm.CreateDecryptor(), input, output);
  740. private static RijndaelManaged Aes256Provider(byte[] key)
  741. {
  742. var k = key ?? Empty; // AesFill(key);
  743. var p = new RijndaelManaged();
  744. p.Key = k;
  745. p.Mode = CipherMode.ECB;
  746. p.Padding = PaddingMode.PKCS7;
  747. return p;
  748. }
  749. /// <summary>对数据进行 AES 加密。</summary>
  750. public static byte[] Aes256Encrypt(byte[] bytes, byte[] key = null)
  751. {
  752. if (bytes == null) return Empty;
  753. if (bytes.Length == 0) return Empty;
  754. var rm = Aes256Provider(key);
  755. var result = new byte[0];
  756. var ct = rm.CreateEncryptor();
  757. try
  758. {
  759. result = ct.TransformFinalBlock(bytes, 0, bytes.Length);
  760. }
  761. catch { }
  762. return result;
  763. }
  764. /// <summary>对数据进行 AES 解密。</summary>
  765. public static byte[] Aes256Decrypt(byte[] cipher, byte[] key = null)
  766. {
  767. if (cipher == null) return Empty;
  768. if (cipher.Length == 0) return Empty;
  769. var rm = Aes256Provider(key);
  770. var result = new byte[0];
  771. var ct = rm.CreateDecryptor();
  772. try
  773. {
  774. result = ct.TransformFinalBlock(cipher, 0, cipher.Length);
  775. }
  776. catch { }
  777. ct.Dispose();
  778. return result;
  779. }
  780. private static readonly byte[] AesDefaultIV = new byte[16];
  781. static T UseAes<T>(byte[] key, byte[] iv, CipherMode cipherMode, PaddingMode paddingMode, Func<RijndaelManaged, T> callback)
  782. {
  783. if (key == null) throw new ArgumentNullException(nameof(key));
  784. var keySize = key.Length * 8;
  785. switch (keySize)
  786. {
  787. case 128:
  788. case 192:
  789. case 256:
  790. break;
  791. default:
  792. throw new ArgumentException($"密钥大小【{keySize}】bits 无效。");
  793. }
  794. if (iv == null) iv = AesDefaultIV;
  795. var ivSize = iv.Length * 8;
  796. if (ivSize != 128) throw new ArgumentException($"初始化向量【{ivSize}】bits 无效。");
  797. using (var rijndael = new RijndaelManaged())
  798. {
  799. rijndael.Key = key;
  800. rijndael.IV = iv;
  801. rijndael.Mode = cipherMode;
  802. rijndael.Padding = paddingMode;
  803. return callback.Invoke(rijndael);
  804. }
  805. }
  806. static void UseAes(Stream input, Stream output, byte[] key, byte[] iv, CipherMode cipherMode, PaddingMode paddingMode, Func<RijndaelManaged, ICryptoTransform> create)
  807. {
  808. if (input == null) throw new ArgumentNullException(nameof(input));
  809. if (output == null) throw new ArgumentNullException(nameof(output));
  810. UseAes<object>(key, iv, cipherMode, paddingMode, rijndael =>
  811. {
  812. using (var transformer = create.Invoke(rijndael))
  813. {
  814. using (var stream = new CryptoStream(output, transformer, CryptoStreamMode.Write))
  815. {
  816. Read(input, stream);
  817. stream.Close();
  818. }
  819. }
  820. return null;
  821. });
  822. }
  823. static byte[] UseAes(byte[] input, byte[] key, byte[] iv, CipherMode cipherMode, PaddingMode paddingMode, Func<RijndaelManaged, ICryptoTransform> create)
  824. {
  825. if (input == null) input = new byte[0];
  826. var result = null as byte[];
  827. return UseAes<byte[]>(key, iv, cipherMode, paddingMode, rijndael =>
  828. {
  829. using (var transformer = create.Invoke(rijndael))
  830. {
  831. var result = transformer.TransformFinalBlock(input, 0, input.Length);
  832. return result;
  833. }
  834. });
  835. }
  836. /// <summary>执行 AES 128/192/256 加密。</summary>
  837. /// <param name="plain">明文。</param>
  838. /// <param name="key">密钥。</param>
  839. /// <param name="cipherMode">块密码模式。</param>
  840. /// <param name="paddingMode">填充模式。</param>
  841. /// <returns></returns>
  842. /// <exception cref="ArgumentNullException" />
  843. /// <exception cref="ArgumentException" />
  844. /// <exception cref="CryptographicException" />
  845. public static byte[] AesEncrypt(byte[] plain, byte[] key, CipherMode cipherMode = CipherMode.ECB, PaddingMode paddingMode = PaddingMode.PKCS7)
  846. {
  847. return UseAes(plain, key, null, cipherMode, paddingMode, (rijndael) => rijndael.CreateEncryptor(rijndael.Key, rijndael.IV));
  848. }
  849. /// <summary>执行 AES 128/192/256 加密。</summary>
  850. /// <param name="plain">明文。</param>
  851. /// <param name="key">密钥,长度必须是 128 位(16 字节)、192 位(24 字节)或 256 位(32 字节)。</param>
  852. /// <param name="iv">初始化向量,必须是 128 位(16 字节)。</param>
  853. /// <param name="cipherMode">块密码模式。</param>
  854. /// <param name="paddingMode">填充模式。</param>
  855. /// <returns></returns>
  856. /// <exception cref="ArgumentNullException" />
  857. /// <exception cref="ArgumentException" />
  858. /// <exception cref="CryptographicException" />
  859. public static byte[] AesEncrypt(byte[] plain, byte[] key, byte[] iv, CipherMode cipherMode = CipherMode.ECB, PaddingMode paddingMode = PaddingMode.PKCS7)
  860. {
  861. return UseAes(plain, key, iv, cipherMode, paddingMode, (rijndael) => rijndael.CreateEncryptor(rijndael.Key, rijndael.IV));
  862. }
  863. /// <summary>执行 AES 128/192/256 解密。</summary>
  864. /// <param name="cipher">密文。</param>
  865. /// <param name="key">密钥,长度必须是 128 位(16 字节)、192 位(24 字节)或 256 位(32 字节)。</param>
  866. /// <param name="cipherMode">块密码模式。</param>
  867. /// <param name="paddingMode">填充模式。</param>
  868. /// <returns></returns>
  869. /// <exception cref="ArgumentNullException" />
  870. /// <exception cref="ArgumentException" />
  871. /// <exception cref="CryptographicException" />
  872. public static byte[] AesDecrypt(byte[] cipher, byte[] key, CipherMode cipherMode = CipherMode.ECB, PaddingMode paddingMode = PaddingMode.PKCS7)
  873. {
  874. return UseAes(cipher, key, null, cipherMode, paddingMode, (rijndael) => rijndael.CreateEncryptor(rijndael.Key, rijndael.IV));
  875. }
  876. /// <summary>执行 AES 128/192/256 解密。</summary>
  877. /// <param name="cipher">密文。</param>
  878. /// <param name="key">密钥,长度必须是 128 位(16 字节)、192 位(24 字节)或 256 位(32 字节)。</param>
  879. /// <param name="iv">初始化向量,必须是 128 位(16 字节)。</param>
  880. /// <param name="cipherMode">块密码模式。</param>
  881. /// <param name="paddingMode">填充模式。</param>
  882. /// <returns></returns>
  883. /// <exception cref="ArgumentNullException" />
  884. /// <exception cref="ArgumentException" />
  885. /// <exception cref="CryptographicException" />
  886. public static byte[] AesDecrypt(byte[] cipher, byte[] key, byte[] iv, CipherMode cipherMode = CipherMode.ECB, PaddingMode paddingMode = PaddingMode.PKCS7)
  887. {
  888. return UseAes(cipher, key, iv, cipherMode, paddingMode, (rijndael) => rijndael.CreateEncryptor(rijndael.Key, rijndael.IV));
  889. }
  890. #endregion
  891. #region Hash
  892. private static byte[] ComputeHash<T>(byte[] bytes) where T : HashAlgorithm, new()
  893. {
  894. if (bytes != null)
  895. {
  896. try
  897. {
  898. using (var algorithm = new T())
  899. {
  900. var result = algorithm.ComputeHash(bytes);
  901. algorithm.Clear();
  902. return result;
  903. }
  904. }
  905. catch { }
  906. }
  907. return Empty;
  908. }
  909. private static byte[] ComputeHash<T>(Stream stream, Action<long> progress) where T : HashAlgorithm, new()
  910. {
  911. if (progress == null)
  912. {
  913. using (var algorithm = new T())
  914. {
  915. if (stream == null)
  916. {
  917. var result = algorithm.ComputeHash(Empty);
  918. algorithm.Clear();
  919. return result;
  920. }
  921. else
  922. {
  923. var result = algorithm.ComputeHash(stream);
  924. algorithm.Clear();
  925. return result;
  926. }
  927. }
  928. }
  929. else
  930. {
  931. if (stream == null) return Empty;
  932. // 初始化。
  933. using (var algorithm = new T())
  934. {
  935. algorithm.Initialize();
  936. // 读取。
  937. var count = 0;
  938. var input = new byte[DefaultBuffer];
  939. var output = new byte[DefaultBuffer];
  940. while (true)
  941. {
  942. count = stream.Read(input, 0, DefaultBuffer);
  943. if (count < DefaultBuffer)
  944. {
  945. algorithm.TransformFinalBlock(input, 0, count);
  946. break;
  947. }
  948. else
  949. {
  950. algorithm.TransformBlock(input, 0, count, output, 0);
  951. }
  952. }
  953. var result = algorithm.Hash;
  954. algorithm.Clear();
  955. return result;
  956. }
  957. }
  958. }
  959. /// <summary>获取 MD5 值。</summary>
  960. public static byte[] MD5(this byte[] bytes) => ComputeHash<MD5CryptoServiceProvider>(bytes);
  961. /// <summary>获取 MD5 值。</summary>
  962. public static byte[] MD5(this Stream stream, Action<long> progress = null) => ComputeHash<MD5CryptoServiceProvider>(stream, progress);
  963. /// <summary>获取 SHA1 值。</summary>
  964. public static byte[] SHA1(this byte[] bytes) => ComputeHash<SHA1CryptoServiceProvider>(bytes);
  965. /// <summary>获取 SHA1 值。</summary>
  966. public static byte[] SHA1(this Stream stream, Action<long> progress = null) => ComputeHash<SHA1CryptoServiceProvider>(stream, progress);
  967. /// <summary>获取 SHA256 值。</summary>
  968. public static byte[] SHA256(this byte[] bytes) => ComputeHash<SHA256CryptoServiceProvider>(bytes);
  969. /// <summary>获取 SHA256 值。</summary>
  970. public static byte[] SHA256(this Stream stream, Action<long> progress = null) => ComputeHash<SHA256CryptoServiceProvider>(stream, progress);
  971. /// <summary>获取 SHA512 值。</summary>
  972. public static byte[] SHA512(this byte[] bytes) => ComputeHash<SHA512CryptoServiceProvider>(bytes);
  973. /// <summary>获取 SHA512 值。</summary>
  974. public static byte[] SHA512(this Stream stream, Action<long> progress = null) => ComputeHash<SHA512CryptoServiceProvider>(stream, progress);
  975. /// <summary>计算 CRC16 校验和。</summary>
  976. public static ushort CRC16(this byte[] bytes) => Internals.CRC16.Instance.Compute(bytes);
  977. #endregion
  978. }
  979. }