SevenZipCompressor.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. using SharpCompress.Archives;
  2. using SharpCompress.Common;
  3. using SharpCompress.Writers;
  4. using System;
  5. using System.Collections.Concurrent;
  6. using System.Collections.Generic;
  7. using System.IO;
  8. using System.Linq;
  9. using System.Net.Http;
  10. using System.Text;
  11. using System.Threading.Tasks;
  12. using System.Web;
  13. using Masuit.Tools.Systems;
  14. namespace Masuit.Tools.Files
  15. {
  16. /// <summary>
  17. /// 7z压缩
  18. /// </summary>
  19. public class SevenZipCompressor : ISevenZipCompressor
  20. {
  21. private readonly HttpClient _httpClient;
  22. /// <summary>
  23. ///
  24. /// </summary>
  25. /// <param name="httpClient"></param>
  26. public SevenZipCompressor(HttpClient httpClient)
  27. {
  28. _httpClient = httpClient;
  29. }
  30. /// <summary>
  31. /// 将多个文件压缩到一个文件流中,可保存为zip文件,方便于web方式下载
  32. /// </summary>
  33. /// <param name="files">多个文件路径,文件或文件夹,或网络路径http/https</param>
  34. /// <param name="rootdir"></param>
  35. /// <param name="archiveType"></param>
  36. /// <returns>文件流</returns>
  37. public MemoryStream ZipStream(IEnumerable<string> files, string rootdir = "", ArchiveType archiveType = ArchiveType.Zip)
  38. {
  39. using var archive = CreateZipArchive(files, rootdir, archiveType);
  40. var ms = new MemoryStream();
  41. archive.SaveTo(ms, new WriterOptions(CompressionType.LZMA)
  42. {
  43. LeaveStreamOpen = true,
  44. ArchiveEncoding = new ArchiveEncoding()
  45. {
  46. Default = Encoding.UTF8
  47. }
  48. });
  49. return ms;
  50. }
  51. /// <summary>
  52. /// 将多个文件压缩到一个文件流中,可保存为zip文件,方便于web方式下载
  53. /// </summary>
  54. /// <param name="streams">多个文件流</param>
  55. /// <param name="archiveType"></param>
  56. /// <param name="disposeAllStreams">是否需要释放所有流</param>
  57. /// <returns>文件流</returns>
  58. public MemoryStream ZipStream(DisposableDictionary<string, Stream> streams, ArchiveType archiveType = ArchiveType.Zip, bool disposeAllStreams = false)
  59. {
  60. using var archive = ArchiveFactory.Create(archiveType);
  61. foreach (var pair in streams)
  62. {
  63. archive.AddEntry(pair.Key, pair.Value, true);
  64. }
  65. var ms = new MemoryStream();
  66. archive.SaveTo(ms, new WriterOptions(CompressionType.LZMA)
  67. {
  68. LeaveStreamOpen = true,
  69. ArchiveEncoding = new ArchiveEncoding()
  70. {
  71. Default = Encoding.UTF8
  72. }
  73. });
  74. if (disposeAllStreams)
  75. {
  76. streams.Dispose();
  77. }
  78. return ms;
  79. }
  80. /// <summary>
  81. /// 压缩多个文件
  82. /// </summary>
  83. /// <param name="files">多个文件路径,文件或文件夹</param>
  84. /// <param name="zipFile">压缩到...</param>
  85. /// <param name="rootdir">压缩包内部根文件夹</param>
  86. /// <param name="archiveType"></param>
  87. public void Zip(IEnumerable<string> files, string zipFile, string rootdir = "", ArchiveType archiveType = ArchiveType.Zip)
  88. {
  89. using var archive = CreateZipArchive(files, rootdir, archiveType);
  90. archive.SaveTo(zipFile, new WriterOptions(CompressionType.LZMA)
  91. {
  92. LeaveStreamOpen = true,
  93. ArchiveEncoding = new ArchiveEncoding()
  94. {
  95. Default = Encoding.UTF8
  96. }
  97. });
  98. }
  99. /// <summary>
  100. /// 压缩多个文件
  101. /// </summary>
  102. /// <param name="streams">多个文件流</param>
  103. /// <param name="zipFile">压缩到...</param>
  104. /// <param name="archiveType"></param>
  105. /// <param name="disposeAllStreams">是否需要释放所有流</param>
  106. public void Zip(DisposableDictionary<string, Stream> streams, string zipFile, ArchiveType archiveType = ArchiveType.Zip, bool disposeAllStreams = false)
  107. {
  108. using var archive = ArchiveFactory.Create(archiveType);
  109. foreach (var pair in streams)
  110. {
  111. archive.AddEntry(pair.Key, pair.Value, true);
  112. }
  113. archive.SaveTo(zipFile, new WriterOptions(CompressionType.LZMA)
  114. {
  115. LeaveStreamOpen = true,
  116. ArchiveEncoding = new ArchiveEncoding()
  117. {
  118. Default = Encoding.UTF8
  119. }
  120. });
  121. if (disposeAllStreams)
  122. {
  123. streams.Dispose();
  124. }
  125. }
  126. /// <summary>
  127. /// 解压文件,自动检测压缩包类型
  128. /// </summary>
  129. /// <param name="compressedFile">rar文件</param>
  130. /// <param name="dir">解压到...</param>
  131. /// <param name="ignoreEmptyDir">忽略空文件夹</param>
  132. public void Decompress(string compressedFile, string dir, bool ignoreEmptyDir = true)
  133. {
  134. if (string.IsNullOrEmpty(dir))
  135. {
  136. dir = Path.GetDirectoryName(compressedFile);
  137. }
  138. ArchiveFactory.WriteToDirectory(compressedFile, Directory.CreateDirectory(dir).FullName, new ExtractionOptions()
  139. {
  140. ExtractFullPath = true,
  141. Overwrite = true
  142. });
  143. }
  144. /// <summary>
  145. /// 创建zip包
  146. /// </summary>
  147. /// <param name="files"></param>
  148. /// <param name="rootdir"></param>
  149. /// <param name="archiveType"></param>
  150. /// <returns></returns>
  151. private IWritableArchive CreateZipArchive(IEnumerable<string> files, string rootdir, ArchiveType archiveType)
  152. {
  153. var archive = ArchiveFactory.Create(archiveType);
  154. var dic = GetFileEntryMaps(files);
  155. var remoteUrls = files.Distinct().Where(s => s.StartsWith("http")).Select(s =>
  156. {
  157. try
  158. {
  159. return new Uri(s);
  160. }
  161. catch (UriFormatException)
  162. {
  163. return null;
  164. }
  165. }).Where(u => u != null).ToList();
  166. foreach (var pair in dic)
  167. {
  168. archive.AddEntry(Path.Combine(rootdir, pair.Value), pair.Key);
  169. }
  170. if (remoteUrls.Any())
  171. {
  172. var streams = new ConcurrentDictionary<string, Stream>();
  173. Parallel.ForEach(remoteUrls, url =>
  174. {
  175. _httpClient.GetAsync(url).ContinueWith(async t =>
  176. {
  177. if (t.IsCompleted)
  178. {
  179. var res = await t;
  180. if (res.IsSuccessStatusCode)
  181. {
  182. Stream stream = await res.Content.ReadAsStreamAsync();
  183. streams[Path.Combine(rootdir, Path.GetFileName(HttpUtility.UrlDecode(url.AbsolutePath)))] = stream;
  184. }
  185. }
  186. }).Wait();
  187. });
  188. foreach (var pair in streams)
  189. {
  190. archive.AddEntry(pair.Key, pair.Value, true);
  191. }
  192. }
  193. return archive;
  194. }
  195. /// <summary>
  196. /// 获取文件路径和zip-entry的映射
  197. /// </summary>
  198. /// <param name="files"></param>
  199. /// <returns></returns>
  200. private Dictionary<string, string> GetFileEntryMaps(IEnumerable<string> files)
  201. {
  202. var fileList = new List<string>();
  203. void GetFilesRecurs(string path)
  204. {
  205. //遍历目标文件夹的所有文件
  206. fileList.AddRange(Directory.GetFiles(path));
  207. //遍历目标文件夹的所有文件夹
  208. foreach (var directory in Directory.GetDirectories(path))
  209. {
  210. GetFilesRecurs(directory);
  211. }
  212. }
  213. files.Where(s => !s.StartsWith("http")).ForEach(s =>
  214. {
  215. if (Directory.Exists(s))
  216. {
  217. GetFilesRecurs(s);
  218. }
  219. else
  220. {
  221. fileList.Add(s);
  222. }
  223. });
  224. if (!fileList.Any())
  225. {
  226. return new Dictionary<string, string>();
  227. }
  228. var dirname = new string(fileList.First().Substring(0, fileList.Min(s => s.Length)).TakeWhile((c, i) => fileList.All(s => s[i] == c)).ToArray());
  229. if (!Directory.Exists(dirname))
  230. {
  231. dirname = Directory.GetParent(dirname).FullName;
  232. }
  233. var dic = fileList.ToDictionary(s => s, s => s.Substring(dirname.Length));
  234. return dic;
  235. }
  236. }
  237. }