SevenZipCompressor.cs 84 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057
  1. /* This file is part of SevenZipSharp.
  2. SevenZipSharp is free software: you can redistribute it and/or modify
  3. it under the terms of the GNU Lesser General Public License as published by
  4. the Free Software Foundation, either version 3 of the License, or
  5. (at your option) any later version.
  6. SevenZipSharp is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU Lesser General Public License for more details.
  10. You should have received a copy of the GNU Lesser General Public License
  11. along with SevenZipSharp. If not, see <http://www.gnu.org/licenses/>.
  12. */
  13. using System;
  14. using System.Collections.Generic;
  15. using System.Globalization;
  16. using System.IO;
  17. #if DOTNET20
  18. using System.Threading;
  19. #else
  20. using System.Linq;
  21. #endif
  22. using System.Runtime.InteropServices;
  23. #if !WINCE
  24. using System.Security.Permissions;
  25. #endif
  26. using SevenZip.Sdk;
  27. using SevenZip.Sdk.Compression.Lzma;
  28. #if MONO
  29. using SevenZip.Mono.COM;
  30. #endif
  31. namespace SevenZip
  32. {
  33. #if COMPRESS
  34. /// <summary>
  35. /// Class for packing files into 7-zip archives
  36. /// </summary>
  37. public sealed partial class SevenZipCompressor
  38. #if UNMANAGED
  39. : SevenZipBase
  40. #endif
  41. {
  42. #if UNMANAGED
  43. #region Fields
  44. private bool _compressingFilesOnDisk;
  45. /// <summary>
  46. /// Gets or sets the archiving compression level.
  47. /// </summary>
  48. public CompressionLevel CompressionLevel { get; set; }
  49. private OutArchiveFormat _archiveFormat = OutArchiveFormat.SevenZip;
  50. private CompressionMethod _compressionMethod = CompressionMethod.Default;
  51. /// <summary>
  52. /// Gets the custom compression parameters - for advanced users only.
  53. /// </summary>
  54. public Dictionary<string, string> CustomParameters { get; private set; }
  55. private int _volumeSize;
  56. private string _archiveName;
  57. /// <summary>
  58. /// Gets or sets the value indicating whether to include empty directories to archives. Default is true.
  59. /// </summary>
  60. public bool IncludeEmptyDirectories { get; set; }
  61. /// <summary>
  62. /// Gets or sets the value indicating whether to preserve the directory root for CompressDirectory.
  63. /// </summary>
  64. public bool PreserveDirectoryRoot { get; set; }
  65. /// <summary>
  66. /// Gets or sets the value indicating whether to preserve the directory structure.
  67. /// </summary>
  68. public bool DirectoryStructure { get; set; }
  69. private bool _directoryCompress;
  70. /// <summary>
  71. /// Gets or sets the compression mode.
  72. /// </summary>
  73. public CompressionMode CompressionMode { get; set; }
  74. private UpdateData _updateData;
  75. private uint _oldFilesCount;
  76. /// <summary>
  77. /// Gets or sets the value indicating whether to encrypt 7-Zip archive headers.
  78. /// </summary>
  79. public bool EncryptHeaders { get; set; }
  80. /// <summary>
  81. /// 获取或设置该值指示是否将压缩文件只开放式写作。
  82. /// Gets or sets the value indicating whether to compress files only open for writing.
  83. /// </summary>
  84. public bool ScanOnlyWritable { get; set; }
  85. /// <summary>
  86. /// Gets or sets the encryption method for zip archives.
  87. /// </summary>
  88. public ZipEncryptionMethod ZipEncryptionMethod { get; set; }
  89. /// <summary>
  90. /// Gets or sets the temporary folder path.
  91. /// </summary>
  92. public string TempFolderPath { get; set; }
  93. /// <summary>
  94. /// Gets or sets the default archive item name used when an item to be compressed has no name,
  95. /// for example, when you compress a MemoryStream instance.
  96. /// </summary>
  97. public string DefaultItemName { get; set; }
  98. /// <summary>
  99. /// Gets or sets the value indicating whether to compress as fast as possible, without calling events.
  100. /// </summary>
  101. public bool FastCompression { get; set; }
  102. #endregion
  103. #endif
  104. private static int _lzmaDictionarySize = 1 << 22;
  105. #if UNMANAGED
  106. private void CommonInit()
  107. {
  108. DirectoryStructure = true;
  109. IncludeEmptyDirectories = true;
  110. CompressionLevel = CompressionLevel.Normal;
  111. CompressionMode = CompressionMode.Create;
  112. ZipEncryptionMethod = ZipEncryptionMethod.ZipCrypto;
  113. CustomParameters = new Dictionary<string, string>();
  114. _updateData = new UpdateData();
  115. DefaultItemName = "default";
  116. }
  117. /// <summary>
  118. /// Initializes a new instance of the SevenZipCompressor class.
  119. /// </summary>
  120. public SevenZipCompressor()
  121. {
  122. try
  123. {
  124. #if !WINCE
  125. TempFolderPath = Path.GetTempPath();
  126. //TempFolderPath = Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.User);
  127. #else
  128. TempFolderPath = "Temp";
  129. #endif
  130. }
  131. catch (System.Security.SecurityException) // Registry access is not allowed, etc.
  132. {
  133. /*throw new SevenZipCompressionFailedException(
  134. "Attempted to get TEMP environment variable but registry access was not allowed (security settings on your machine). You must call SevenZipCompressor constructor overload with your own temporary path.");
  135. */
  136. throw new SevenZipCompressionFailedException(
  137. "Path.GetTempPath() threw a System.Security.SecurityException. You must call SevenZipCompressor constructor overload with your own temporary path.");
  138. }
  139. CommonInit();
  140. }
  141. /// <summary>
  142. /// Initializes a new instance of the SevenZipCompressor class.
  143. /// </summary>
  144. /// <param name="temporaryPath">Your own temporary path (default is set in the parameterless constructor overload.)</param>
  145. public SevenZipCompressor(string temporaryPath)
  146. {
  147. TempFolderPath = temporaryPath;
  148. if (!Directory.Exists(TempFolderPath))
  149. {
  150. try
  151. {
  152. Directory.CreateDirectory(TempFolderPath);
  153. }
  154. catch (Exception)
  155. {
  156. throw new SevenZipCompressionFailedException("The specified temporary path is invalid.");
  157. }
  158. }
  159. CommonInit();
  160. }
  161. #endif
  162. /// <summary>
  163. /// Checks if the specified stream supports compression.
  164. /// </summary>
  165. /// <param name="stream">The stream to check.</param>
  166. private static void ValidateStream(Stream stream)
  167. {
  168. if (!stream.CanWrite || !stream.CanSeek)
  169. {
  170. throw new ArgumentException("The specified stream can not seek or is not writable.", "stream");
  171. }
  172. }
  173. #if UNMANAGED
  174. #region Private functions
  175. private IOutArchive MakeOutArchive(IInStream inArchiveStream)
  176. {
  177. IInArchive inArchive = SevenZipLibraryManager.InArchive(
  178. Formats.InForOutFormats[_archiveFormat], this);
  179. using (ArchiveOpenCallback openCallback = GetArchiveOpenCallback())
  180. {
  181. ulong checkPos = 1 << 15;
  182. if (inArchive.Open(inArchiveStream, ref checkPos, openCallback) != (int) OperationResult.Ok)
  183. {
  184. if (
  185. !ThrowException(null, new SevenZipArchiveException("Can not update the archive: Open() failed.")))
  186. {
  187. return null;
  188. }
  189. }
  190. _oldFilesCount = inArchive.GetNumberOfItems();
  191. }
  192. return (IOutArchive) inArchive;
  193. }
  194. /// <summary>
  195. /// Guaranties the correct work of the SetCompressionProperties function
  196. /// </summary>
  197. /// <param name="method">The compression method to check</param>
  198. /// <returns>The value indicating whether the specified method is valid for the current ArchiveFormat</returns>
  199. private bool MethodIsValid(CompressionMethod method)
  200. {
  201. if (method == CompressionMethod.Default)
  202. {
  203. return true;
  204. }
  205. switch (_archiveFormat)
  206. {
  207. case OutArchiveFormat.Zip:
  208. return method != CompressionMethod.Ppmd;
  209. case OutArchiveFormat.GZip:
  210. return method == CompressionMethod.Deflate;
  211. case OutArchiveFormat.BZip2:
  212. return method == CompressionMethod.BZip2;
  213. case OutArchiveFormat.SevenZip:
  214. return method != CompressionMethod.Deflate && method != CompressionMethod.Deflate64;
  215. case OutArchiveFormat.Tar:
  216. return method == CompressionMethod.Copy;
  217. default:
  218. return true;
  219. }
  220. }
  221. private bool SwitchIsInCustomParameters(string name)
  222. {
  223. return CustomParameters.ContainsKey(name);
  224. }
  225. /// <summary>
  226. /// Sets the compression properties
  227. /// </summary>
  228. private void SetCompressionProperties()
  229. {
  230. switch (_archiveFormat)
  231. {
  232. case OutArchiveFormat.Tar:
  233. break;
  234. default:
  235. ISetProperties setter = CompressionMode == CompressionMode.Create && _updateData.FileNamesToModify == null
  236. ? (ISetProperties) SevenZipLibraryManager.OutArchive(
  237. _archiveFormat, this)
  238. : (ISetProperties) SevenZipLibraryManager.InArchive(
  239. Formats.InForOutFormats[_archiveFormat], this);
  240. if (setter == null)
  241. {
  242. if (!ThrowException(null,
  243. new CompressionFailedException(
  244. "The specified archive format is unsupported.")))
  245. {
  246. return;
  247. }
  248. }
  249. if (_volumeSize > 0 && ArchiveFormat != OutArchiveFormat.SevenZip)
  250. {
  251. throw new CompressionFailedException("Unfortunately, the creation of multivolume non-7Zip archives is not implemented. It will be one day, though.");
  252. }
  253. if (CustomParameters.ContainsKey("x") || CustomParameters.ContainsKey("m"))
  254. {
  255. if (
  256. !ThrowException(null,
  257. new CompressionFailedException(
  258. "The specified compression parameters are invalid.")))
  259. {
  260. return;
  261. }
  262. }
  263. var names = new List<IntPtr>(2 + CustomParameters.Count);
  264. var values = new List<PropVariant>(2 + CustomParameters.Count);
  265. #if !WINCE
  266. var sp = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
  267. sp.Demand();
  268. #endif
  269. #region Initialize compression properties
  270. if (_compressionMethod == CompressionMethod.Default)
  271. {
  272. names.Add(Marshal.StringToBSTR("x"));
  273. values.Add(new PropVariant());
  274. foreach (var pair in CustomParameters)
  275. {
  276. names.Add(Marshal.StringToBSTR(pair.Key));
  277. var pv = new PropVariant();
  278. if (pair.Key == "fb" || pair.Key == "pass" || pair.Key == "d")
  279. {
  280. pv.VarType = VarEnum.VT_UI4;
  281. pv.UInt32Value = Convert.ToUInt32(pair.Value, CultureInfo.InvariantCulture);
  282. }
  283. else
  284. {
  285. pv.VarType = VarEnum.VT_BSTR;
  286. pv.Value = Marshal.StringToBSTR(pair.Value);
  287. }
  288. values.Add(pv);
  289. }
  290. }
  291. else
  292. {
  293. names.Add(Marshal.StringToBSTR("x"));
  294. names.Add(_archiveFormat == OutArchiveFormat.Zip
  295. ? Marshal.StringToBSTR("m")
  296. : Marshal.StringToBSTR("0"));
  297. values.Add(new PropVariant());
  298. var pv = new PropVariant
  299. {
  300. VarType = VarEnum.VT_BSTR,
  301. Value = Marshal.StringToBSTR(Formats.MethodNames[_compressionMethod])
  302. };
  303. values.Add(pv);
  304. foreach (var pair in CustomParameters)
  305. {
  306. names.Add(Marshal.StringToBSTR(pair.Key));
  307. pv = new PropVariant();
  308. if (pair.Key == "fb" || pair.Key == "pass" || pair.Key == "d")
  309. {
  310. pv.VarType = VarEnum.VT_UI4;
  311. pv.UInt32Value = Convert.ToUInt32(pair.Value, CultureInfo.InvariantCulture);
  312. }
  313. else
  314. {
  315. pv.VarType = VarEnum.VT_BSTR;
  316. pv.Value = Marshal.StringToBSTR(pair.Value);
  317. }
  318. values.Add(pv);
  319. }
  320. }
  321. #endregion
  322. #region Set compression level
  323. PropVariant clpv = values[0];
  324. clpv.VarType = VarEnum.VT_UI4;
  325. switch (CompressionLevel)
  326. {
  327. case CompressionLevel.None:
  328. clpv.UInt32Value = 0;
  329. break;
  330. case CompressionLevel.Fast:
  331. clpv.UInt32Value = 1;
  332. break;
  333. case CompressionLevel.Low:
  334. clpv.UInt32Value = 3;
  335. break;
  336. case CompressionLevel.Normal:
  337. clpv.UInt32Value = 5;
  338. break;
  339. case CompressionLevel.High:
  340. clpv.UInt32Value = 7;
  341. break;
  342. case CompressionLevel.Ultra:
  343. clpv.UInt32Value = 9;
  344. break;
  345. }
  346. values[0] = clpv;
  347. #endregion
  348. #region Encrypt headers
  349. if (EncryptHeaders && _archiveFormat == OutArchiveFormat.SevenZip &&
  350. !SwitchIsInCustomParameters("he"))
  351. {
  352. names.Add(Marshal.StringToBSTR("he"));
  353. var tmp = new PropVariant {VarType = VarEnum.VT_BSTR, Value = Marshal.StringToBSTR("on")};
  354. values.Add(tmp);
  355. }
  356. #endregion
  357. #region Zip Encryption
  358. if (_archiveFormat == OutArchiveFormat.Zip && ZipEncryptionMethod != ZipEncryptionMethod.ZipCrypto &&
  359. !SwitchIsInCustomParameters("em"))
  360. {
  361. names.Add(Marshal.StringToBSTR("em"));
  362. var tmp = new PropVariant
  363. {
  364. VarType = VarEnum.VT_BSTR,
  365. Value = Marshal.StringToBSTR(
  366. #if !WINCE
  367. Enum.GetName(typeof (ZipEncryptionMethod), ZipEncryptionMethod))
  368. #else
  369. OpenNETCF.Enum2.GetName(typeof (ZipEncryptionMethod), ZipEncryptionMethod))
  370. #endif
  371. };
  372. values.Add(tmp);
  373. }
  374. #endregion
  375. var namesHandle = GCHandle.Alloc(names.ToArray(), GCHandleType.Pinned);
  376. var valuesHandle = GCHandle.Alloc(values.ToArray(), GCHandleType.Pinned);
  377. try
  378. {
  379. if (setter != null) //ReSharper
  380. setter.SetProperties(namesHandle.AddrOfPinnedObject(), valuesHandle.AddrOfPinnedObject(),
  381. names.Count);
  382. }
  383. finally
  384. {
  385. namesHandle.Free();
  386. valuesHandle.Free();
  387. }
  388. break;
  389. }
  390. }
  391. /// <summary>
  392. /// Finds the common root of file names
  393. /// </summary>
  394. /// <param name="files">Array of file names</param>
  395. /// <returns>Common root</returns>
  396. private static int CommonRoot(ICollection<string> files)
  397. {
  398. var splittedFileNames = new List<string[]>(files.Count);
  399. #if CS4
  400. splittedFileNames.AddRange(files.Select(fn => fn.Split(Path.DirectorySeparatorChar)));
  401. #else
  402. foreach (string fn in files)
  403. {
  404. splittedFileNames.Add(fn.Split(Path.DirectorySeparatorChar));
  405. }
  406. #endif
  407. int minSplitLength = splittedFileNames[0].Length - 1;
  408. if (files.Count > 1)
  409. {
  410. for (int i = 1; i < files.Count; i++)
  411. {
  412. if (minSplitLength > splittedFileNames[i].Length)
  413. {
  414. minSplitLength = splittedFileNames[i].Length;
  415. }
  416. }
  417. }
  418. string res = "";
  419. for (int i = 0; i < minSplitLength; i++)
  420. {
  421. bool common = true;
  422. for (int j = 1; j < files.Count; j++)
  423. {
  424. if (!(common &= splittedFileNames[j - 1][i] == splittedFileNames[j][i]))
  425. {
  426. break;
  427. }
  428. }
  429. if (common)
  430. {
  431. res += splittedFileNames[0][i] + Path.DirectorySeparatorChar;
  432. }
  433. else
  434. {
  435. break;
  436. }
  437. }
  438. return res.Length;
  439. }
  440. /// <summary>
  441. /// Validates the common root
  442. /// </summary>
  443. /// <param name="commonRootLength">The length of the common root of the file names.</param>
  444. /// <param name="files">Array of file names</param>
  445. private static void CheckCommonRoot(string[] files, ref int commonRootLength)
  446. {
  447. string commonRoot;
  448. try
  449. {
  450. commonRoot = files[0].Substring(0, commonRootLength);
  451. }
  452. catch (ArgumentOutOfRangeException)
  453. {
  454. throw new SevenZipInvalidFileNamesException("invalid common root.");
  455. }
  456. if (commonRoot.EndsWith(new string(Path.DirectorySeparatorChar, 1), StringComparison.CurrentCulture))
  457. {
  458. commonRoot = commonRoot.Substring(0, commonRootLength - 1);
  459. commonRootLength--;
  460. }
  461. #if CS4
  462. if (files.Any(fn => !fn.StartsWith(commonRoot, StringComparison.CurrentCulture)))
  463. {
  464. throw new SevenZipInvalidFileNamesException("invalid common root.");
  465. }
  466. #else
  467. foreach (string fn in files)
  468. {
  469. if (!fn.StartsWith(commonRoot, StringComparison.CurrentCulture))
  470. {
  471. throw new SevenZipInvalidFileNamesException("invalid common root.");
  472. }
  473. }
  474. #endif
  475. }
  476. /// <summary>
  477. /// Ensures that directory directory is not empty
  478. /// </summary>
  479. /// <param name="directory">Directory name</param>
  480. /// <returns>False if is not empty</returns>
  481. private static bool RecursiveDirectoryEmptyCheck(string directory)
  482. {
  483. var di = new DirectoryInfo(directory);
  484. if (di.GetFiles().Length > 0)
  485. {
  486. return false;
  487. }
  488. bool empty = true;
  489. foreach (DirectoryInfo cdi in di.GetDirectories())
  490. {
  491. empty &= RecursiveDirectoryEmptyCheck(cdi.FullName);
  492. if (!empty)
  493. {
  494. return false;
  495. }
  496. }
  497. return true;
  498. }
  499. /// <summary>
  500. /// Makes special FileInfo array for the archive file table.
  501. /// </summary>
  502. /// <param name="files">Array of files to pack.</param>
  503. /// <param name="commonRootLength">The length of the common root of file names</param>
  504. /// <param name="directoryCompress">The value indicating whether to produce the array for files in a particular directory or just for an array of files.</param>
  505. /// <param name="directoryStructure">Preserve directory structure.</param>
  506. /// <returns>Special FileInfo array for the archive file table.</returns>
  507. private static FileInfo[] ProduceFileInfoArray(
  508. string[] files, int commonRootLength,
  509. bool directoryCompress, bool directoryStructure)
  510. {
  511. var fis = new List<FileInfo>(files.Length);
  512. string commonRoot = files[0].Substring(0, commonRootLength);
  513. if (directoryCompress)
  514. {
  515. #if CS4
  516. fis.AddRange(files.Select(fn => new FileInfo(fn)));
  517. #else
  518. foreach (string fn in files)
  519. {
  520. fis.Add(new FileInfo(fn));
  521. }
  522. #endif
  523. }
  524. else
  525. {
  526. if (!directoryStructure)
  527. {
  528. #if CS4
  529. fis.AddRange(from fn in files where !Directory.Exists(fn) select new FileInfo(fn));
  530. #else
  531. foreach (string fn in files)
  532. {
  533. if (!Directory.Exists(fn))
  534. {
  535. fis.Add(new FileInfo(fn));
  536. }
  537. }
  538. #endif
  539. }
  540. else
  541. {
  542. var fns = new List<string>(files.Length);
  543. CheckCommonRoot(files, ref commonRootLength);
  544. if (commonRootLength > 0)
  545. {
  546. commonRootLength++;
  547. foreach (string f in files)
  548. {
  549. string[] splittedAfn = f.Substring(commonRootLength).Split(Path.DirectorySeparatorChar);
  550. string cfn = commonRoot;
  551. foreach (string t in splittedAfn) {
  552. cfn += Path.DirectorySeparatorChar + t;
  553. if (!fns.Contains(cfn))
  554. {
  555. fis.Add(new FileInfo(cfn));
  556. fns.Add(cfn);
  557. }
  558. }
  559. }
  560. }
  561. else
  562. {
  563. foreach (string f in files)
  564. {
  565. string[] splittedAfn = f.Substring(commonRootLength).Split(Path.DirectorySeparatorChar);
  566. string cfn = splittedAfn[0];
  567. for (int i = 1; i < splittedAfn.Length; i++)
  568. {
  569. cfn += Path.DirectorySeparatorChar + splittedAfn[i];
  570. if (!fns.Contains(cfn))
  571. {
  572. fis.Add(new FileInfo(cfn));
  573. fns.Add(cfn);
  574. }
  575. }
  576. }
  577. }
  578. }
  579. }
  580. return fis.ToArray();
  581. }
  582. /// <summary>
  583. /// Recursive function for adding files in directory
  584. /// </summary>
  585. /// <param name="directory">Directory directory</param>
  586. /// <param name="files">List of files</param>
  587. /// <param name="searchPattern">Search string, such as "*.txt"</param>
  588. private void AddFilesFromDirectory(string directory, ICollection<string> files, string searchPattern)
  589. {
  590. var di = new DirectoryInfo(directory);
  591. foreach (FileInfo fi in di.GetFiles(searchPattern))
  592. {
  593. if (!ScanOnlyWritable)
  594. {
  595. files.Add(fi.FullName);
  596. }
  597. else
  598. {
  599. try
  600. {
  601. using (fi.OpenWrite()) {}
  602. files.Add(fi.FullName);
  603. }
  604. catch (IOException) {}
  605. }
  606. }
  607. foreach (DirectoryInfo cdi in di.GetDirectories())
  608. {
  609. if (IncludeEmptyDirectories)
  610. {
  611. files.Add(cdi.FullName);
  612. }
  613. AddFilesFromDirectory(cdi.FullName, files, searchPattern);
  614. }
  615. }
  616. #endregion
  617. #region GetArchiveUpdateCallback overloads
  618. /// <summary>
  619. /// Performs the common ArchiveUpdateCallback initialization.
  620. /// </summary>
  621. /// <param name="auc">The ArchiveUpdateCallback instance to initialize.</param>
  622. private void CommonUpdateCallbackInit(ArchiveUpdateCallback auc)
  623. {
  624. auc.FileCompressionStarted += FileCompressionStartedEventProxy;
  625. auc.Compressing += CompressingEventProxy;
  626. auc.FileCompressionFinished += FileCompressionFinishedEventProxy;
  627. auc.DefaultItemName = DefaultItemName;
  628. auc.FastCompression = FastCompression;
  629. }
  630. private float GetDictionarySize()
  631. {
  632. float dictionarySize = 0.001f;
  633. switch (_compressionMethod)
  634. {
  635. case CompressionMethod.Default:
  636. case CompressionMethod.Lzma:
  637. case CompressionMethod.Lzma2:
  638. switch (CompressionLevel)
  639. {
  640. case CompressionLevel.None:
  641. dictionarySize = 0.001f;
  642. break;
  643. case CompressionLevel.Fast:
  644. dictionarySize = 1.0f / 16 * 7.5f + 4;
  645. break;
  646. case CompressionLevel.Low:
  647. dictionarySize = 7.5f * 11.5f + 4;
  648. break;
  649. case CompressionLevel.Normal:
  650. dictionarySize = 16 * 11.5f + 4;
  651. break;
  652. case CompressionLevel.High:
  653. dictionarySize = 32 * 11.5f + 4;
  654. break;
  655. case CompressionLevel.Ultra:
  656. dictionarySize = 64 * 11.5f + 4;
  657. break;
  658. }
  659. break;
  660. case CompressionMethod.BZip2:
  661. switch (CompressionLevel)
  662. {
  663. case CompressionLevel.None:
  664. dictionarySize = 0;
  665. break;
  666. case CompressionLevel.Fast:
  667. dictionarySize = 0.095f;
  668. break;
  669. case CompressionLevel.Low:
  670. dictionarySize = 0.477f;
  671. break;
  672. case CompressionLevel.Normal:
  673. case CompressionLevel.High:
  674. case CompressionLevel.Ultra:
  675. dictionarySize = 0.858f;
  676. break;
  677. }
  678. break;
  679. case CompressionMethod.Deflate:
  680. case CompressionMethod.Deflate64:
  681. dictionarySize = 32;
  682. break;
  683. case CompressionMethod.Ppmd:
  684. dictionarySize = 16;
  685. break;
  686. }
  687. return dictionarySize;
  688. }
  689. /// <summary>
  690. /// Produces a new instance of ArchiveUpdateCallback class.
  691. /// </summary>
  692. /// <param name="files">Array of FileInfo - files to pack</param>
  693. /// <param name="rootLength">Length of the common root of file names</param>
  694. /// <param name="password">The archive password</param>
  695. /// <returns></returns>
  696. private ArchiveUpdateCallback GetArchiveUpdateCallback(
  697. FileInfo[] files, int rootLength, string password)
  698. {
  699. SetCompressionProperties();
  700. var auc = (String.IsNullOrEmpty(password))
  701. ? new ArchiveUpdateCallback(files, rootLength, this, GetUpdateData(), DirectoryStructure)
  702. { DictionarySize = GetDictionarySize() }
  703. : new ArchiveUpdateCallback(files, rootLength, password, this, GetUpdateData(), DirectoryStructure)
  704. { DictionarySize = GetDictionarySize() };
  705. CommonUpdateCallbackInit(auc);
  706. return auc;
  707. }
  708. /// <summary>
  709. /// Produces a new instance of ArchiveUpdateCallback class.
  710. /// </summary>
  711. /// <param name="inStream">The archive input stream.</param>
  712. /// <param name="password">The archive password.</param>
  713. /// <returns></returns>
  714. private ArchiveUpdateCallback GetArchiveUpdateCallback(Stream inStream, string password)
  715. {
  716. SetCompressionProperties();
  717. var auc = (String.IsNullOrEmpty(password))
  718. ? new ArchiveUpdateCallback(inStream, this, GetUpdateData(), DirectoryStructure)
  719. { DictionarySize = GetDictionarySize() }
  720. : new ArchiveUpdateCallback(inStream, password, this, GetUpdateData(), DirectoryStructure)
  721. { DictionarySize = GetDictionarySize() };
  722. CommonUpdateCallbackInit(auc);
  723. return auc;
  724. }
  725. /// <summary>
  726. /// Produces a new instance of ArchiveUpdateCallback class.
  727. /// </summary>
  728. /// <param name="streamDict">Dictionary&lt;name of the archive entry, stream&gt;.</param>
  729. /// <param name="password">The archive password</param>
  730. /// <returns></returns>
  731. private ArchiveUpdateCallback GetArchiveUpdateCallback(
  732. Dictionary<string, Stream> streamDict, string password)
  733. {
  734. SetCompressionProperties();
  735. var auc = (String.IsNullOrEmpty(password))
  736. ? new ArchiveUpdateCallback(streamDict, this, GetUpdateData(), DirectoryStructure)
  737. { DictionarySize = GetDictionarySize() }
  738. : new ArchiveUpdateCallback(streamDict, password, this, GetUpdateData(), DirectoryStructure)
  739. { DictionarySize = GetDictionarySize() };
  740. CommonUpdateCallbackInit(auc);
  741. return auc;
  742. }
  743. #endregion
  744. #region Service "Get" functions
  745. private void FreeCompressionCallback(ArchiveUpdateCallback callback)
  746. {
  747. callback.FileCompressionStarted -= FileCompressionStartedEventProxy;
  748. callback.Compressing -= CompressingEventProxy;
  749. callback.FileCompressionFinished -= FileCompressionFinishedEventProxy;
  750. }
  751. private string GetTempArchiveFileName(string archiveName)
  752. {
  753. return Path.Combine(TempFolderPath, Path.GetFileName(archiveName) + ".~");
  754. }
  755. private FileStream GetArchiveFileStream(string archiveName)
  756. {
  757. if ((CompressionMode != CompressionMode.Create || _updateData.FileNamesToModify != null) && !File.Exists(archiveName))
  758. {
  759. if (
  760. !ThrowException(null, new CompressionFailedException("file \"" + archiveName + "\" does not exist.")))
  761. {
  762. return null;
  763. }
  764. }
  765. return _volumeSize == 0
  766. ? CompressionMode == CompressionMode.Create && _updateData.FileNamesToModify == null
  767. ? File.Create(archiveName)
  768. : File.Create(GetTempArchiveFileName(archiveName))
  769. : null;
  770. }
  771. private void FinalizeUpdate()
  772. {
  773. if (_volumeSize == 0 && (CompressionMode != CompressionMode.Create || _updateData.FileNamesToModify != null))
  774. {
  775. File.Move(GetTempArchiveFileName(_archiveName), _archiveName);
  776. }
  777. }
  778. private UpdateData GetUpdateData()
  779. {
  780. if (_updateData.FileNamesToModify == null)
  781. {
  782. var updateData = new UpdateData {Mode = (InternalCompressionMode) ((int) CompressionMode)};
  783. switch (CompressionMode)
  784. {
  785. case CompressionMode.Create:
  786. updateData.FilesCount = UInt32.MaxValue;
  787. break;
  788. case CompressionMode.Append:
  789. updateData.FilesCount = _oldFilesCount;
  790. break;
  791. }
  792. return updateData;
  793. }
  794. return _updateData;
  795. }
  796. private ISequentialOutStream GetOutStream(Stream outStream)
  797. {
  798. if (!_compressingFilesOnDisk)
  799. {
  800. return new OutStreamWrapper(outStream, false);
  801. }
  802. if (_volumeSize == 0 || CompressionMode != CompressionMode.Create || _updateData.FileNamesToModify != null)
  803. {
  804. return new OutStreamWrapper(outStream, true);
  805. }
  806. return new OutMultiStreamWrapper(_archiveName, _volumeSize);
  807. }
  808. private IInStream GetInStream()
  809. {
  810. return File.Exists(_archiveName) &&
  811. (CompressionMode != CompressionMode.Create && _compressingFilesOnDisk || _updateData.FileNamesToModify != null)
  812. ?
  813. new InStreamWrapper(
  814. new FileStream(_archiveName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite),
  815. true)
  816. : null;
  817. }
  818. private ArchiveOpenCallback GetArchiveOpenCallback()
  819. {
  820. return String.IsNullOrEmpty(Password)
  821. ?
  822. new ArchiveOpenCallback(_archiveName)
  823. :
  824. new ArchiveOpenCallback(_archiveName, Password);
  825. }
  826. #endregion
  827. #region Core public Members
  828. #region Events
  829. /// <summary>
  830. /// 当下一个文件打包时发生
  831. /// Occurs when the next file is going to be packed.
  832. /// </summary>
  833. /// <remarks>当7-zip引擎请求下一个文件的输入流打包时发生Occurs when 7-zip engine requests for an input stream for the next file to pack it</remarks>
  834. public event EventHandler<FileNameEventArgs> FileCompressionStarted;
  835. /// <summary>
  836. /// 压缩当前文件时发生。
  837. /// Occurs when the current file was compressed.
  838. /// </summary>
  839. public event EventHandler<EventArgs> FileCompressionFinished;
  840. /// <summary>
  841. /// 数据压缩时发生
  842. /// Occurs when data are being compressed
  843. /// </summary>
  844. /// <remarks>Use this event for accurate progress handling and various ProgressBar.StepBy(e.PercentDelta) routines</remarks>
  845. public event EventHandler<ProgressEventArgs> Compressing;
  846. /// <summary>
  847. /// 当所有文件信息确定并且SevenZipCompressor即将开始压缩它们时发生。
  848. /// Occurs when all files information was determined and SevenZipCompressor is about to start to compress them.
  849. /// </summary>
  850. /// <remarks>The incoming int value indicates the number of scanned files.</remarks>
  851. public event EventHandler<IntEventArgs> FilesFound;
  852. /// <summary>
  853. /// 压缩过程完成时发生
  854. /// Occurs when the compression procedure is finished
  855. /// </summary>
  856. public event EventHandler<EventArgs> CompressionFinished;
  857. #region Event proxies
  858. /// <summary>
  859. /// Event proxy for FileCompressionStarted.
  860. /// </summary>
  861. /// <param name="sender">The sender of the event.</param>
  862. /// <param name="e">The event arguments.</param>
  863. private void FileCompressionStartedEventProxy(object sender, FileNameEventArgs e)
  864. {
  865. OnEvent(FileCompressionStarted, e, false);
  866. }
  867. /// <summary>
  868. /// Event proxy for FileCompressionFinished.
  869. /// </summary>
  870. /// <param name="sender">The sender of the event.</param>
  871. /// <param name="e">The event arguments.</param>
  872. private void FileCompressionFinishedEventProxy(object sender, EventArgs e)
  873. {
  874. OnEvent(FileCompressionFinished, e, false);
  875. }
  876. /// <summary>
  877. /// Event proxy for Compressing.
  878. /// </summary>
  879. /// <param name="sender">The sender of the event.</param>
  880. /// <param name="e">The event arguments.</param>
  881. private void CompressingEventProxy(object sender, ProgressEventArgs e)
  882. {
  883. OnEvent(Compressing, e, false);
  884. }
  885. /// <summary>
  886. /// Event proxy for FilesFound.
  887. /// </summary>
  888. /// <param name="sender">The sender of the event.</param>
  889. /// <param name="e">The event arguments.</param>
  890. private void FilesFoundEventProxy(object sender, IntEventArgs e)
  891. {
  892. OnEvent(FilesFound, e, false);
  893. }
  894. #endregion
  895. #endregion
  896. #region Properties
  897. /// <summary>
  898. /// Gets or sets the archive format
  899. /// </summary>
  900. public OutArchiveFormat ArchiveFormat
  901. {
  902. get
  903. {
  904. return _archiveFormat;
  905. }
  906. set
  907. {
  908. _archiveFormat = value;
  909. if (!MethodIsValid(_compressionMethod))
  910. {
  911. _compressionMethod = CompressionMethod.Default;
  912. }
  913. }
  914. }
  915. /// <summary>
  916. /// Gets or sets the compression method
  917. /// </summary>
  918. public CompressionMethod CompressionMethod
  919. {
  920. get
  921. {
  922. return _compressionMethod;
  923. }
  924. set
  925. {
  926. _compressionMethod = !MethodIsValid(value) ? CompressionMethod.Default : value;
  927. }
  928. }
  929. /// <summary>
  930. /// 获取或设置(0表示无卷)在归档量的字节大小。
  931. /// Gets or sets the size in bytes of an archive volume (0 for no volumes).
  932. /// </summary>
  933. public int VolumeSize
  934. {
  935. get
  936. {
  937. return _volumeSize;
  938. }
  939. set
  940. {
  941. _volumeSize = value > 0 ? value : 0;
  942. }
  943. }
  944. #endregion
  945. #region CompressFiles overloads
  946. /// <summary>
  947. /// 包文件到存档。
  948. /// Packs files into the archive.
  949. /// </summary>
  950. /// <param name="fileFullNames">Array of file names to pack.</param>
  951. /// <param name="archiveName">The archive file name.</param>
  952. public void CompressFiles(
  953. string archiveName, params string[] fileFullNames)
  954. {
  955. CompressFilesEncrypted(archiveName, "", fileFullNames);
  956. }
  957. /// <summary>
  958. /// Packs files into the archive.
  959. /// </summary>
  960. /// <param name="fileFullNames">Array of file names to pack.</param>
  961. /// <param name="archiveStream">The archive output stream.
  962. /// Use CompressFiles(string archiveName ... ) overloads for archiving to disk.</param>
  963. public void CompressFiles(
  964. Stream archiveStream, params string[] fileFullNames)
  965. {
  966. CompressFilesEncrypted(archiveStream, "", fileFullNames);
  967. }
  968. /// <summary>
  969. /// Packs files into the archive.
  970. /// </summary>
  971. /// <param name="fileFullNames">Array of file names to pack.</param>
  972. /// <param name="commonRootLength">The length of the common root of the file names.</param>
  973. /// <param name="archiveName">The archive file name.</param>
  974. public void CompressFiles(
  975. string archiveName, int commonRootLength, params string[] fileFullNames)
  976. {
  977. CompressFilesEncrypted(archiveName, commonRootLength, "", fileFullNames);
  978. }
  979. /// <summary>
  980. /// Packs files into the archive.
  981. /// </summary>
  982. /// <param name="fileFullNames">Array of file names to pack.</param>
  983. /// <param name="commonRootLength">The length of the common root of the file names.</param>
  984. /// <param name="archiveStream">The archive output stream.
  985. /// Use CompressFiles(string archiveName, ... ) overloads for archiving to disk.</param>
  986. public void CompressFiles(
  987. Stream archiveStream, int commonRootLength, params string[] fileFullNames)
  988. {
  989. CompressFilesEncrypted(archiveStream, commonRootLength, "", fileFullNames);
  990. }
  991. /// <summary>
  992. /// Packs files into the archive.
  993. /// </summary>
  994. /// <param name="fileFullNames">Array of file names to pack.</param>
  995. /// <param name="archiveName">The archive file name.</param>
  996. /// <param name="password">The archive password.</param>
  997. public void CompressFilesEncrypted(
  998. string archiveName, string password, params string[] fileFullNames)
  999. {
  1000. CompressFilesEncrypted(archiveName, CommonRoot(fileFullNames), password, fileFullNames);
  1001. }
  1002. /// <summary>
  1003. /// Packs files into the archive.
  1004. /// </summary>
  1005. /// <param name="fileFullNames">Array of file names to pack.</param>
  1006. /// <param name="archiveStream">The archive output stream.
  1007. /// Use CompressFiles( ... string archiveName ... ) overloads for archiving to disk.</param>
  1008. /// <param name="password">The archive password.</param>
  1009. public void CompressFilesEncrypted(
  1010. Stream archiveStream, string password, params string[] fileFullNames)
  1011. {
  1012. CompressFilesEncrypted(archiveStream, CommonRoot(fileFullNames), password, fileFullNames);
  1013. }
  1014. /// <summary>
  1015. /// Packs files into the archive.
  1016. /// </summary>
  1017. /// <param name="fileFullNames">Array of file names to pack.</param>
  1018. /// <param name="commonRootLength">The length of the common root of the file names.</param>
  1019. /// <param name="archiveName">The archive file name.</param>
  1020. /// <param name="password">The archive password.</param>
  1021. public void CompressFilesEncrypted(
  1022. string archiveName, int commonRootLength, string password, params string[] fileFullNames)
  1023. {
  1024. _compressingFilesOnDisk = true;
  1025. _archiveName = archiveName;
  1026. using (FileStream fs = GetArchiveFileStream(archiveName))
  1027. {
  1028. if (fs == null)
  1029. {
  1030. return;
  1031. }
  1032. CompressFilesEncrypted(fs, commonRootLength, password, fileFullNames);
  1033. }
  1034. FinalizeUpdate();
  1035. }
  1036. /// <summary>
  1037. /// Packs files into the archive.
  1038. /// </summary>
  1039. /// <param name="fileFullNames">Array of file names to pack.</param>
  1040. /// <param name="commonRootLength">The length of the common root of the file names.</param>
  1041. /// <param name="archiveStream">The archive output stream.
  1042. /// Use CompressFiles( ... string archiveName ... ) overloads for archiving to disk.</param>
  1043. /// <param name="password">The archive password.</param>
  1044. public void CompressFilesEncrypted(
  1045. Stream archiveStream, int commonRootLength, string password, params string[] fileFullNames)
  1046. {
  1047. ClearExceptions();
  1048. if (fileFullNames.Length > 1 &&
  1049. (_archiveFormat == OutArchiveFormat.BZip2 || _archiveFormat == OutArchiveFormat.GZip ||
  1050. _archiveFormat == OutArchiveFormat.XZ))
  1051. {
  1052. if (!ThrowException(null,
  1053. new CompressionFailedException("Can not compress more than one file in this format.")))
  1054. {
  1055. return;
  1056. }
  1057. }
  1058. if (_volumeSize == 0 || !_compressingFilesOnDisk)
  1059. {
  1060. ValidateStream(archiveStream);
  1061. }
  1062. FileInfo[] files = null;
  1063. try
  1064. {
  1065. files = ProduceFileInfoArray(fileFullNames, commonRootLength, _directoryCompress, DirectoryStructure);
  1066. }
  1067. catch (Exception e)
  1068. {
  1069. if (!ThrowException(null, e))
  1070. {
  1071. return;
  1072. }
  1073. }
  1074. _directoryCompress = false;
  1075. if (FilesFound != null)
  1076. {
  1077. FilesFound(this, new IntEventArgs(fileFullNames.Length));
  1078. }
  1079. try
  1080. {
  1081. ISequentialOutStream sequentialArchiveStream;
  1082. using ((sequentialArchiveStream = GetOutStream(archiveStream)) as IDisposable)
  1083. {
  1084. IInStream inArchiveStream;
  1085. using ((inArchiveStream = GetInStream()) as IDisposable)
  1086. {
  1087. IOutArchive outArchive;
  1088. if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
  1089. {
  1090. SevenZipLibraryManager.LoadLibrary(this, _archiveFormat);
  1091. outArchive = SevenZipLibraryManager.OutArchive(_archiveFormat, this);
  1092. }
  1093. else
  1094. {
  1095. // Create IInArchive, read it and convert to IOutArchive
  1096. SevenZipLibraryManager.LoadLibrary(
  1097. this, Formats.InForOutFormats[_archiveFormat]);
  1098. if ((outArchive = MakeOutArchive(inArchiveStream)) == null)
  1099. {
  1100. return;
  1101. }
  1102. }
  1103. using (var auc = GetArchiveUpdateCallback(files, commonRootLength, password))
  1104. {
  1105. try
  1106. {
  1107. if (files != null) //ReSharper
  1108. CheckedExecute(
  1109. outArchive.UpdateItems(
  1110. sequentialArchiveStream, (uint) files.Length + _oldFilesCount, auc),
  1111. SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
  1112. }
  1113. finally
  1114. {
  1115. FreeCompressionCallback(auc);
  1116. }
  1117. }
  1118. }
  1119. }
  1120. }
  1121. finally
  1122. {
  1123. if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
  1124. {
  1125. SevenZipLibraryManager.FreeLibrary(this, _archiveFormat);
  1126. }
  1127. else
  1128. {
  1129. SevenZipLibraryManager.FreeLibrary(this, Formats.InForOutFormats[_archiveFormat]);
  1130. File.Delete(_archiveName);
  1131. }
  1132. _compressingFilesOnDisk = false;
  1133. OnEvent(CompressionFinished, EventArgs.Empty, false);
  1134. }
  1135. ThrowUserException();
  1136. }
  1137. #endregion
  1138. #region CompressDirectory overloads
  1139. #if !CS4
  1140. /// <summary>
  1141. /// Recursively packs all files in the specified directory.
  1142. /// </summary>
  1143. /// <param name="directory">The directory to compress.</param>
  1144. /// <param name="archiveName">The archive file name.</param>
  1145. public void CompressDirectory(
  1146. string directory, string archiveName)
  1147. {
  1148. CompressDirectory(directory, archiveName, "", "*", true);
  1149. }
  1150. /// <summary>
  1151. /// Recursively packs all files in the specified directory.
  1152. /// </summary>
  1153. /// <param name="directory">The directory to compress.</param>
  1154. /// <param name="archiveStream">The archive output stream.
  1155. /// Use CompressDirectory( ... string archiveName ... ) overloads for archiving to disk.</param>
  1156. public void CompressDirectory(
  1157. string directory, Stream archiveStream)
  1158. {
  1159. CompressDirectory(directory, archiveStream, "", "*", true);
  1160. }
  1161. /// <summary>
  1162. /// Recursively packs all files in the specified directory.
  1163. /// </summary>
  1164. /// <param name="directory">The directory to compress.</param>
  1165. /// <param name="archiveName">The archive file name.</param>
  1166. /// <param name="password">The archive password.</param>
  1167. public void CompressDirectory(
  1168. string directory, string archiveName, string password)
  1169. {
  1170. CompressDirectory(directory, archiveName, password, "*", true);
  1171. }
  1172. /// <summary>
  1173. /// Recursively packs all files in the specified directory.
  1174. /// </summary>
  1175. /// <param name="directory">The directory to compress.</param>
  1176. /// <param name="archiveStream">The archive output stream.
  1177. /// Use CompressDirectory( ... string archiveName ... ) overloads for archiving to disk.</param>
  1178. /// <param name="password">The archive password.</param>
  1179. public void CompressDirectory(
  1180. string directory, Stream archiveStream, string password)
  1181. {
  1182. CompressDirectory(directory, archiveStream, password, "*", true);
  1183. }
  1184. /// <summary>
  1185. /// Packs all files in the specified directory.
  1186. /// </summary>
  1187. /// <param name="directory">The directory to compress.</param>
  1188. /// <param name="archiveName">The archive file name.</param>
  1189. /// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
  1190. public void CompressDirectory(
  1191. string directory, string archiveName, bool recursion)
  1192. {
  1193. CompressDirectory(directory, archiveName, "", "*", recursion);
  1194. }
  1195. /// <summary>
  1196. /// Packs all files in the specified directory.
  1197. /// </summary>
  1198. /// <param name="directory">The directory to compress.</param>
  1199. /// <param name="archiveStream">The archive output stream.
  1200. /// Use CompressDirectory( ... string archiveName ... ) overloads for archiving to disk.</param>
  1201. /// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
  1202. public void CompressDirectory(
  1203. string directory, Stream archiveStream, bool recursion)
  1204. {
  1205. CompressDirectory(directory, archiveStream, "", "*", recursion);
  1206. }
  1207. /// <summary>
  1208. /// Packs all files found by the specified pattern in the specified directory.
  1209. /// </summary>
  1210. /// <param name="directory">The directory to compress.</param>
  1211. /// <param name="archiveName">The archive file name.</param>
  1212. /// <param name="searchPattern">Search string, such as "*.txt".</param>
  1213. /// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
  1214. public void CompressDirectory(
  1215. string directory, string archiveName,
  1216. string searchPattern, bool recursion)
  1217. {
  1218. CompressDirectory(directory, archiveName, "", searchPattern, recursion);
  1219. }
  1220. /// <summary>
  1221. /// Packs all files found by the specified pattern in the specified directory.
  1222. /// </summary>
  1223. /// <param name="directory">The directory to compress.</param>
  1224. /// <param name="archiveStream">The archive output stream.
  1225. /// Use CompressDirectory( ... string archiveName ... ) overloads for archiving to disk.</param>
  1226. /// <param name="searchPattern">Search string, such as "*.txt".</param>
  1227. /// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
  1228. public void CompressDirectory(
  1229. string directory, Stream archiveStream,
  1230. string searchPattern, bool recursion)
  1231. {
  1232. CompressDirectory(directory, archiveStream, "", searchPattern, recursion);
  1233. }
  1234. /// <summary>
  1235. /// Packs all files in the specified directory.
  1236. /// </summary>
  1237. /// <param name="directory">The directory to compress.</param>
  1238. /// <param name="archiveName">The archive file name.</param>
  1239. /// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
  1240. /// <param name="password">The archive password.</param>
  1241. public void CompressDirectory(
  1242. string directory, string archiveName,
  1243. bool recursion, string password)
  1244. {
  1245. CompressDirectory(directory, archiveName, password, "*", recursion);
  1246. }
  1247. /// <summary>
  1248. /// Packs all files in the specified directory.
  1249. /// </summary>
  1250. /// <param name="directory">The directory to compress.</param>
  1251. /// <param name="archiveStream">The archive output stream.
  1252. /// Use CompressDirectory( ... string archiveName ... ) overloads for archiving to disk.</param>
  1253. /// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
  1254. /// <param name="password">The archive password.</param>
  1255. public void CompressDirectory(
  1256. string directory, Stream archiveStream,
  1257. bool recursion, string password)
  1258. {
  1259. CompressDirectory(directory, archiveStream, password, "*", recursion);
  1260. }
  1261. #endif
  1262. #if !CS4
  1263. /// <summary>
  1264. /// Packs all files in the specified directory.
  1265. /// </summary>
  1266. /// <param name="directory">The directory to compress.</param>
  1267. /// <param name="archiveName">The archive file name.</param>
  1268. /// <param name="password">The archive password.</param>
  1269. /// <param name="searchPattern">Search string, such as "*.txt".</param>
  1270. /// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
  1271. public void CompressDirectory(
  1272. string directory, string archiveName,
  1273. string password, string searchPattern, bool recursion)
  1274. #else
  1275. /// <summary>
  1276. /// 包中的指定目录下的所有文件。
  1277. /// Packs all files in the specified directory.
  1278. /// </summary>
  1279. /// <param name="directory">The directory to compress.</param>
  1280. /// <param name="archiveName">The archive file name.</param>
  1281. /// <param name="password">The archive password.</param>
  1282. /// <param name="searchPattern">Search string, such as "*.txt".</param>
  1283. /// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
  1284. public void CompressDirectory(
  1285. string directory, string archiveName,
  1286. string password = "", string searchPattern = "*", bool recursion = true)
  1287. #endif
  1288. {
  1289. _compressingFilesOnDisk = true;
  1290. _archiveName = archiveName;
  1291. using (FileStream fs = GetArchiveFileStream(archiveName))
  1292. {
  1293. if (fs == null && _volumeSize == 0)
  1294. {
  1295. return;
  1296. }
  1297. CompressDirectory(directory, fs, password, searchPattern, recursion);
  1298. }
  1299. FinalizeUpdate();
  1300. }
  1301. #if !CS4
  1302. /// <summary>
  1303. /// Packs all files in the specified directory.
  1304. /// </summary>
  1305. /// <param name="directory">The directory to compress.</param>
  1306. /// <param name="archiveStream">The archive output stream.
  1307. /// Use CompressDirectory( ... string archiveName ... ) overloads for archiving to disk.</param>
  1308. /// <param name="password">The archive password.</param>
  1309. /// <param name="searchPattern">Search string, such as "*.txt".</param>
  1310. /// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
  1311. public void CompressDirectory(
  1312. string directory, Stream archiveStream,
  1313. string password, string searchPattern, bool recursion)
  1314. #else
  1315. /// <summary>
  1316. /// Packs all files in the specified directory.
  1317. /// </summary>
  1318. /// <param name="directory">The directory to compress.</param>
  1319. /// <param name="archiveStream">The archive output stream.
  1320. /// Use CompressDirectory( ... string archiveName ... ) overloads for archiving to disk.</param>
  1321. /// <param name="password">The archive password.</param>
  1322. /// <param name="searchPattern">Search string, such as "*.txt".</param>
  1323. /// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
  1324. public void CompressDirectory(
  1325. string directory, Stream archiveStream,
  1326. string password = "", string searchPattern = "*", bool recursion = true)
  1327. #endif
  1328. {
  1329. var files = new List<string>();
  1330. if (!Directory.Exists(directory))
  1331. {
  1332. throw new ArgumentException("Directory \"" + directory + "\" does not exist!");
  1333. }
  1334. if (RecursiveDirectoryEmptyCheck(directory))
  1335. {
  1336. throw new SevenZipInvalidFileNamesException("the specified directory is empty!");
  1337. }
  1338. if (recursion)
  1339. {
  1340. AddFilesFromDirectory(directory, files, searchPattern);
  1341. }
  1342. else
  1343. {
  1344. #if CS4
  1345. files.AddRange((new DirectoryInfo(directory)).GetFiles(searchPattern).Select(fi => fi.FullName));
  1346. #else
  1347. foreach (FileInfo fi in (new DirectoryInfo(directory)).GetFiles(searchPattern))
  1348. {
  1349. files.Add(fi.FullName);
  1350. }
  1351. #endif
  1352. }
  1353. int commonRootLength = directory.Length;
  1354. if (directory.EndsWith("\\", StringComparison.OrdinalIgnoreCase))
  1355. {
  1356. directory = directory.Substring(0, directory.Length - 1);
  1357. }
  1358. else
  1359. {
  1360. commonRootLength++;
  1361. }
  1362. if (PreserveDirectoryRoot)
  1363. {
  1364. var upperRoot = Path.GetDirectoryName(directory);
  1365. commonRootLength = upperRoot.Length +
  1366. (upperRoot.EndsWith("\\", StringComparison.OrdinalIgnoreCase) ? 0 : 1);
  1367. }
  1368. _directoryCompress = true;
  1369. CompressFilesEncrypted(archiveStream, commonRootLength, password, files.ToArray());
  1370. }
  1371. #endregion
  1372. #region CompressFileDictionary overloads
  1373. #if !CS4
  1374. /// <summary>
  1375. /// Packs the specified file dictionary.
  1376. /// </summary>
  1377. /// <param name="fileDictionary">Dictionary&lt;name of the archive entry, file name&gt;.
  1378. /// If a file name is null, the corresponding archive entry becomes a directory.</param>
  1379. /// <param name="archiveName">The archive file name.</param>
  1380. public void CompressFileDictionary(
  1381. Dictionary<string, string> fileDictionary, string archiveName)
  1382. {
  1383. CompressFileDictionary(fileDictionary, archiveName, "");
  1384. }
  1385. /// <summary>
  1386. /// Packs the specified file dictionary.
  1387. /// </summary>
  1388. /// <param name="fileDictionary">Dictionary&lt;name of the archive entry, file name&gt;.
  1389. /// If a file name is null, the corresponding archive entry becomes a directory.</param>
  1390. /// <param name="archiveStream">The archive output stream.
  1391. /// Use CompressFileDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
  1392. public void CompressFileDictionary(
  1393. Dictionary<string, string> fileDictionary, Stream archiveStream)
  1394. {
  1395. CompressFileDictionary(fileDictionary, archiveStream, "");
  1396. }
  1397. #endif
  1398. #if !CS4
  1399. /// <summary>
  1400. /// Packs the specified file dictionary.
  1401. /// </summary>
  1402. /// <param name="fileDictionary">Dictionary&lt;name of the archive entry, file name&gt;.
  1403. /// If a file name is null, the corresponding archive entry becomes a directory.</param>
  1404. /// <param name="archiveName">The archive file name.</param>
  1405. /// <param name="password">The archive password.</param>
  1406. public void CompressFileDictionary(
  1407. Dictionary<string, string> fileDictionary, string archiveName, string password)
  1408. #else
  1409. /// <summary>
  1410. /// Packs the specified file dictionary.
  1411. /// </summary>
  1412. /// <param name="fileDictionary">Dictionary&lt;name of the archive entry, file name&gt;.
  1413. /// If a file name is null, the corresponding archive entry becomes a directory.</param>
  1414. /// <param name="archiveName">The archive file name.</param>
  1415. /// <param name="password">The archive password.</param>
  1416. public void CompressFileDictionary(
  1417. Dictionary<string, string> fileDictionary, string archiveName, string password = "")
  1418. #endif
  1419. {
  1420. _compressingFilesOnDisk = true;
  1421. _archiveName = archiveName;
  1422. using (FileStream fs = GetArchiveFileStream(archiveName))
  1423. {
  1424. if (fs == null)
  1425. {
  1426. return;
  1427. }
  1428. CompressFileDictionary(fileDictionary, fs, password);
  1429. }
  1430. FinalizeUpdate();
  1431. }
  1432. #if !CS4
  1433. /// <summary>
  1434. /// Packs the specified file dictionary.
  1435. /// </summary>
  1436. /// <param name="fileDictionary">Dictionary&lt;name of the archive entry, file name&gt;.
  1437. /// If a file name is null, the corresponding archive entry becomes a directory.</param>
  1438. /// <param name="archiveStream">The archive output stream.
  1439. /// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
  1440. /// <param name="password">The archive password.</param>
  1441. public void CompressFileDictionary(
  1442. Dictionary<string, string> fileDictionary, Stream archiveStream, string password)
  1443. #else
  1444. /// <summary>
  1445. /// Packs the specified file dictionary.
  1446. /// </summary>
  1447. /// <param name="fileDictionary">Dictionary&lt;name of the archive entry, file name&gt;.
  1448. /// If a file name is null, the corresponding archive entry becomes a directory.</param>
  1449. /// <param name="archiveStream">The archive output stream.
  1450. /// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
  1451. /// <param name="password">The archive password.</param>
  1452. public void CompressFileDictionary(
  1453. Dictionary<string, string> fileDictionary, Stream archiveStream, string password = "")
  1454. #endif
  1455. {
  1456. var streamDict = new Dictionary<string, Stream>(fileDictionary.Count);
  1457. foreach (var pair in fileDictionary)
  1458. {
  1459. if (pair.Value == null)
  1460. {
  1461. streamDict.Add(pair.Key, null);
  1462. }
  1463. else
  1464. {
  1465. if (!File.Exists(pair.Value))
  1466. {
  1467. throw new CompressionFailedException("The file corresponding to the archive entry \"" + pair.Key + "\" does not exist.");
  1468. }
  1469. streamDict.Add(
  1470. pair.Key,
  1471. new FileStream(pair.Value, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
  1472. }
  1473. }
  1474. //The created streams will be automatically disposed inside.
  1475. CompressStreamDictionary(streamDict, archiveStream, password);
  1476. }
  1477. #endregion
  1478. #region CompressStreamDictionary overloads
  1479. #if !CS4
  1480. /// <summary>
  1481. /// Packs the specified stream dictionary.
  1482. /// </summary>
  1483. /// <param name="streamDictionary">Dictionary&lt;name of the archive entry, stream&gt;.
  1484. /// If a stream is null, the corresponding string becomes a directory name.</param>
  1485. /// <param name="archiveName">The archive file name.</param>
  1486. public void CompressStreamDictionary(
  1487. Dictionary<string, Stream> streamDictionary, string archiveName)
  1488. {
  1489. CompressStreamDictionary(streamDictionary, archiveName, "");
  1490. }
  1491. /// <summary>
  1492. /// Packs the specified stream dictionary.
  1493. /// </summary>
  1494. /// <param name="streamDictionary">Dictionary&lt;name of the archive entry, stream&gt;.
  1495. /// If a stream is null, the corresponding string becomes a directory name.</param>
  1496. /// <param name="archiveStream">The archive output stream.
  1497. /// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
  1498. public void CompressStreamDictionary(
  1499. Dictionary<string, Stream> streamDictionary, Stream archiveStream)
  1500. {
  1501. CompressStreamDictionary(streamDictionary, archiveStream, "");
  1502. }
  1503. #endif
  1504. #if !CS4
  1505. /// <summary>
  1506. /// Packs the specified stream dictionary.
  1507. /// </summary>
  1508. /// <param name="streamDictionary">Dictionary&lt;name of the archive entry, stream&gt;.
  1509. /// If a stream is null, the corresponding string becomes a directory name.</param>
  1510. /// <param name="archiveName">The archive file name.</param>
  1511. /// <param name="password">The archive password.</param>
  1512. public void CompressStreamDictionary(
  1513. Dictionary<string, Stream> streamDictionary, string archiveName, string password)
  1514. #else
  1515. /// <summary>
  1516. /// Packs the specified stream dictionary.
  1517. /// </summary>
  1518. /// <param name="streamDictionary">Dictionary&lt;name of the archive entry, stream&gt;.
  1519. /// If a stream is null, the corresponding string becomes a directory name.</param>
  1520. /// <param name="archiveName">The archive file name.</param>
  1521. /// <param name="password">The archive password.</param>
  1522. public void CompressStreamDictionary(
  1523. Dictionary<string, Stream> streamDictionary, string archiveName, string password = "")
  1524. #endif
  1525. {
  1526. _compressingFilesOnDisk = true;
  1527. _archiveName = archiveName;
  1528. using (FileStream fs = GetArchiveFileStream(archiveName))
  1529. {
  1530. if (fs == null)
  1531. {
  1532. return;
  1533. }
  1534. CompressStreamDictionary(streamDictionary, fs, password);
  1535. }
  1536. FinalizeUpdate();
  1537. }
  1538. #if !CS4
  1539. /// <summary>
  1540. /// Packs the specified stream dictionary.
  1541. /// </summary>
  1542. /// <param name="streamDictionary">Dictionary&lt;name of the archive entry, stream&gt;.
  1543. /// If a stream is null, the corresponding string becomes a directory name.</param>
  1544. /// <param name="archiveStream">The archive output stream.
  1545. /// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
  1546. /// <param name="password">The archive password.</param>
  1547. public void CompressStreamDictionary(
  1548. Dictionary<string, Stream> streamDictionary, Stream archiveStream, string password)
  1549. #else
  1550. /// <summary>
  1551. /// Packs the specified stream dictionary.
  1552. /// </summary>
  1553. /// <param name="streamDictionary">Dictionary&lt;name of the archive entry, stream&gt;.
  1554. /// If a stream is null, the corresponding string becomes a directory name.</param>
  1555. /// <param name="archiveStream">The archive output stream.
  1556. /// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
  1557. /// <param name="password">The archive password.</param>
  1558. public void CompressStreamDictionary(
  1559. Dictionary<string, Stream> streamDictionary, Stream archiveStream, string password = "")
  1560. #endif
  1561. {
  1562. ClearExceptions();
  1563. if (streamDictionary.Count > 1 &&
  1564. (_archiveFormat == OutArchiveFormat.BZip2 || _archiveFormat == OutArchiveFormat.GZip ||
  1565. _archiveFormat == OutArchiveFormat.XZ))
  1566. {
  1567. if (!ThrowException(null,
  1568. new CompressionFailedException("Can not compress more than one file/stream in this format.")))
  1569. {
  1570. return;
  1571. }
  1572. }
  1573. if (_volumeSize == 0 || !_compressingFilesOnDisk)
  1574. {
  1575. ValidateStream(archiveStream);
  1576. }
  1577. #if CS4
  1578. if (streamDictionary.Where(
  1579. pair => pair.Value != null && (!pair.Value.CanSeek || !pair.Value.CanRead)).Any(
  1580. pair => !ThrowException(null,
  1581. new ArgumentException("The specified stream dictionary contains an invalid stream corresponding to the archive entry \""
  1582. + pair.Key + "\".", "streamDictionary")))) {
  1583. return;
  1584. }
  1585. #else
  1586. foreach (var pair in streamDictionary)
  1587. {
  1588. if (pair.Value != null && (!pair.Value.CanSeek || !pair.Value.CanRead))
  1589. {
  1590. if (!ThrowException(null, new ArgumentException(
  1591. "The specified stream dictionary contains an invalid stream corresponding to the archive entry \"" + pair.Key + "\".",
  1592. "streamDictionary")))
  1593. {
  1594. return;
  1595. }
  1596. }
  1597. }
  1598. #endif
  1599. try
  1600. {
  1601. ISequentialOutStream sequentialArchiveStream;
  1602. using ((sequentialArchiveStream = GetOutStream(archiveStream)) as IDisposable)
  1603. {
  1604. IInStream inArchiveStream;
  1605. using ((inArchiveStream = GetInStream()) as IDisposable)
  1606. {
  1607. IOutArchive outArchive;
  1608. if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
  1609. {
  1610. SevenZipLibraryManager.LoadLibrary(this, _archiveFormat);
  1611. outArchive = SevenZipLibraryManager.OutArchive(_archiveFormat, this);
  1612. }
  1613. else
  1614. {
  1615. // Create IInArchive, read it and convert to IOutArchive
  1616. SevenZipLibraryManager.LoadLibrary(
  1617. this, Formats.InForOutFormats[_archiveFormat]);
  1618. if ((outArchive = MakeOutArchive(inArchiveStream)) == null)
  1619. {
  1620. return;
  1621. }
  1622. }
  1623. using (ArchiveUpdateCallback auc = GetArchiveUpdateCallback(
  1624. streamDictionary, password))
  1625. {
  1626. try
  1627. {
  1628. CheckedExecute(outArchive.UpdateItems(sequentialArchiveStream,
  1629. (uint)streamDictionary.Count + _oldFilesCount, auc),
  1630. SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
  1631. }
  1632. finally
  1633. {
  1634. FreeCompressionCallback(auc);
  1635. }
  1636. }
  1637. }
  1638. }
  1639. }
  1640. finally
  1641. {
  1642. if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
  1643. {
  1644. SevenZipLibraryManager.FreeLibrary(this, _archiveFormat);
  1645. }
  1646. else
  1647. {
  1648. SevenZipLibraryManager.FreeLibrary(this, Formats.InForOutFormats[_archiveFormat]);
  1649. File.Delete(_archiveName);
  1650. }
  1651. _compressingFilesOnDisk = false;
  1652. OnEvent(CompressionFinished, EventArgs.Empty, false);
  1653. }
  1654. ThrowUserException();
  1655. }
  1656. #endregion
  1657. #region CompressStream overloads
  1658. #if !CS4
  1659. /// <summary>
  1660. /// Compresses the specified stream.
  1661. /// </summary>
  1662. /// <param name="inStream">The source uncompressed stream.</param>
  1663. /// <param name="outStream">The destination compressed stream.</param>
  1664. /// <exception cref="ArgumentException">ArgumentException: at least one of the specified streams is invalid.</exception>
  1665. public void CompressStream(Stream inStream, Stream outStream)
  1666. {
  1667. CompressStream(inStream, outStream, "");
  1668. }
  1669. #endif
  1670. /// <summary>
  1671. /// Compresses the specified stream.
  1672. /// </summary>
  1673. /// <param name="inStream">The source uncompressed stream.</param>
  1674. /// <param name="outStream">The destination compressed stream.</param>
  1675. /// <param name="password">The archive password.</param>
  1676. /// <exception cref="ArgumentException">ArgumentException: at least one of the specified streams is invalid.</exception>
  1677. public void CompressStream(Stream inStream, Stream outStream, string password
  1678. #if CS4
  1679. = ""
  1680. #endif
  1681. )
  1682. {
  1683. ClearExceptions();
  1684. if (!inStream.CanSeek || !inStream.CanRead || !outStream.CanWrite)
  1685. {
  1686. if (!ThrowException(null, new ArgumentException("The specified streams are invalid.")))
  1687. {
  1688. return;
  1689. }
  1690. }
  1691. try
  1692. {
  1693. SevenZipLibraryManager.LoadLibrary(this, _archiveFormat);
  1694. ISequentialOutStream sequentialArchiveStream;
  1695. using ((sequentialArchiveStream = GetOutStream(outStream)) as IDisposable)
  1696. {
  1697. using (ArchiveUpdateCallback auc = GetArchiveUpdateCallback(inStream, password))
  1698. {
  1699. try
  1700. {
  1701. CheckedExecute(
  1702. SevenZipLibraryManager.OutArchive(_archiveFormat, this).UpdateItems(
  1703. sequentialArchiveStream, 1, auc),
  1704. SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
  1705. }
  1706. finally
  1707. {
  1708. FreeCompressionCallback(auc);
  1709. }
  1710. }
  1711. }
  1712. }
  1713. finally
  1714. {
  1715. SevenZipLibraryManager.FreeLibrary(this, _archiveFormat);
  1716. OnEvent(CompressionFinished, EventArgs.Empty, false);
  1717. }
  1718. ThrowUserException();
  1719. }
  1720. #endregion
  1721. #region ModifyArchive overloads
  1722. #if !CS4
  1723. /// <summary>
  1724. /// Modifies the existing archive (renames files or deletes them).
  1725. /// </summary>
  1726. /// <param name="archiveName">The archive file name.</param>
  1727. /// <param name="newFileNames">New file names. Null value to delete the corresponding index.</param>
  1728. public void ModifyArchive(string archiveName, Dictionary<int, string> newFileNames)
  1729. {
  1730. ModifyArchive(archiveName, newFileNames, "");
  1731. }
  1732. #endif
  1733. /// <summary>
  1734. /// Modifies the existing archive (renames files or deletes them).
  1735. /// </summary>
  1736. /// <param name="archiveName">The archive file name.</param>
  1737. /// <param name="newFileNames">New file names. Null value to delete the corresponding index.</param>
  1738. /// <param name="password">The archive password.</param>
  1739. public void ModifyArchive(string archiveName, Dictionary<int, string> newFileNames, string password
  1740. #if CS4
  1741. = ""
  1742. #endif
  1743. )
  1744. {
  1745. ClearExceptions();
  1746. if (!SevenZipLibraryManager.ModifyCapable)
  1747. {
  1748. throw new SevenZipLibraryException("The specified 7zip native library does not support this method.");
  1749. }
  1750. if (!File.Exists(archiveName))
  1751. {
  1752. if (!ThrowException(null, new ArgumentException("The specified archive does not exist.", "archiveName")))
  1753. {
  1754. return;
  1755. }
  1756. }
  1757. if (newFileNames == null || newFileNames.Count == 0)
  1758. {
  1759. if (!ThrowException(null, new ArgumentException("Invalid new file names.", "newFileNames")))
  1760. {
  1761. return;
  1762. }
  1763. }
  1764. try
  1765. {
  1766. using (var extr = new SevenZipExtractor(archiveName))
  1767. {
  1768. _updateData = new UpdateData();
  1769. var archiveData = new ArchiveFileInfo[extr.ArchiveFileData.Count];
  1770. extr.ArchiveFileData.CopyTo(archiveData, 0);
  1771. _updateData.ArchiveFileData = new List<ArchiveFileInfo>(archiveData);
  1772. }
  1773. _updateData.FileNamesToModify = newFileNames;
  1774. _updateData.Mode = InternalCompressionMode.Modify;
  1775. }
  1776. catch (SevenZipException e)
  1777. {
  1778. if (!ThrowException(null, e))
  1779. {
  1780. return;
  1781. }
  1782. }
  1783. try
  1784. {
  1785. ISequentialOutStream sequentialArchiveStream;
  1786. _compressingFilesOnDisk = true;
  1787. using ((sequentialArchiveStream = GetOutStream(GetArchiveFileStream(archiveName))) as IDisposable)
  1788. {
  1789. IInStream inArchiveStream;
  1790. _archiveName = archiveName;
  1791. using ((inArchiveStream = GetInStream()) as IDisposable)
  1792. {
  1793. IOutArchive outArchive;
  1794. // Create IInArchive, read it and convert to IOutArchive
  1795. SevenZipLibraryManager.LoadLibrary(
  1796. this, Formats.InForOutFormats[_archiveFormat]);
  1797. if ((outArchive = MakeOutArchive(inArchiveStream)) == null)
  1798. {
  1799. return;
  1800. }
  1801. using (ArchiveUpdateCallback auc = GetArchiveUpdateCallback(null, 0, password))
  1802. {
  1803. UInt32 deleteCount = 0;
  1804. if (_updateData.FileNamesToModify != null)
  1805. {
  1806. #if CS4 // System.Linq of C# 4 is great
  1807. deleteCount = (UInt32)_updateData.FileNamesToModify.Sum(
  1808. pairDeleted => pairDeleted.Value == null ? 1 : 0);
  1809. #else
  1810. foreach(var pairDeleted in _updateData.FileNamesToModify)
  1811. {
  1812. if (pairDeleted.Value == null)
  1813. {
  1814. deleteCount++;
  1815. }
  1816. }
  1817. #endif
  1818. }
  1819. try
  1820. {
  1821. CheckedExecute(
  1822. outArchive.UpdateItems(
  1823. sequentialArchiveStream, _oldFilesCount - deleteCount, auc),
  1824. SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
  1825. }
  1826. finally
  1827. {
  1828. FreeCompressionCallback(auc);
  1829. }
  1830. }
  1831. }
  1832. }
  1833. }
  1834. finally
  1835. {
  1836. SevenZipLibraryManager.FreeLibrary(this, Formats.InForOutFormats[_archiveFormat]);
  1837. File.Delete(archiveName);
  1838. FinalizeUpdate();
  1839. _compressingFilesOnDisk = false;
  1840. _updateData.FileNamesToModify = null;
  1841. _updateData.ArchiveFileData = null;
  1842. OnEvent(CompressionFinished, EventArgs.Empty, false);
  1843. }
  1844. ThrowUserException();
  1845. }
  1846. #endregion
  1847. #endregion
  1848. #endif
  1849. /// <summary>
  1850. /// Gets or sets the dictionary size for the managed LZMA algorithm.
  1851. /// </summary>
  1852. public static int LzmaDictionarySize
  1853. {
  1854. get
  1855. {
  1856. return _lzmaDictionarySize;
  1857. }
  1858. set
  1859. {
  1860. _lzmaDictionarySize = value;
  1861. }
  1862. }
  1863. internal static void WriteLzmaProperties(Encoder encoder)
  1864. {
  1865. #region LZMA properties definition
  1866. CoderPropId[] propIDs =
  1867. {
  1868. CoderPropId.DictionarySize,
  1869. CoderPropId.PosStateBits,
  1870. CoderPropId.LitContextBits,
  1871. CoderPropId.LitPosBits,
  1872. CoderPropId.Algorithm,
  1873. CoderPropId.NumFastBytes,
  1874. CoderPropId.MatchFinder,
  1875. CoderPropId.EndMarker
  1876. };
  1877. object[] properties =
  1878. {
  1879. _lzmaDictionarySize,
  1880. 2,
  1881. 3,
  1882. 0,
  1883. 2,
  1884. 256,
  1885. "bt4",
  1886. false
  1887. };
  1888. #endregion
  1889. encoder.SetCoderProperties(propIDs, properties);
  1890. }
  1891. /// <summary>
  1892. /// Compresses the specified stream with LZMA algorithm (C# inside)
  1893. /// </summary>
  1894. /// <param name="inStream">The source uncompressed stream</param>
  1895. /// <param name="outStream">The destination compressed stream</param>
  1896. /// <param name="inLength">The length of uncompressed data (null for inStream.Length)</param>
  1897. /// <param name="codeProgressEvent">The event for handling the code progress</param>
  1898. public static void CompressStream(Stream inStream, Stream outStream, int? inLength,
  1899. EventHandler<ProgressEventArgs> codeProgressEvent)
  1900. {
  1901. if (!inStream.CanRead || !outStream.CanWrite)
  1902. {
  1903. throw new ArgumentException("The specified streams are invalid.");
  1904. }
  1905. var encoder = new Encoder();
  1906. WriteLzmaProperties(encoder);
  1907. encoder.WriteCoderProperties(outStream);
  1908. long streamSize = inLength.HasValue ? inLength.Value : inStream.Length;
  1909. for (int i = 0; i < 8; i++)
  1910. {
  1911. outStream.WriteByte((byte) (streamSize >> (8*i)));
  1912. }
  1913. encoder.Code(inStream, outStream, -1, -1, new LzmaProgressCallback(streamSize, codeProgressEvent));
  1914. }
  1915. /// <summary>
  1916. /// Compresses byte array with LZMA algorithm (C# inside)
  1917. /// </summary>
  1918. /// <param name="data">Byte array to compress</param>
  1919. /// <returns>Compressed byte array</returns>
  1920. public static byte[] CompressBytes(byte[] data)
  1921. {
  1922. using (var inStream = new MemoryStream(data))
  1923. {
  1924. using (var outStream = new MemoryStream())
  1925. {
  1926. var encoder = new Encoder();
  1927. WriteLzmaProperties(encoder);
  1928. encoder.WriteCoderProperties(outStream);
  1929. long streamSize = inStream.Length;
  1930. for (int i = 0; i < 8; i++)
  1931. outStream.WriteByte((byte) (streamSize >> (8*i)));
  1932. encoder.Code(inStream, outStream, -1, -1, null);
  1933. return outStream.ToArray();
  1934. }
  1935. }
  1936. }
  1937. }
  1938. #endif
  1939. }