123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025 |
- namespace SevenZip
- {
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Runtime.InteropServices;
- #if NET472 || NETSTANDARD2_0
- using System.Security.Permissions;
- #endif
- using SevenZip.Sdk;
- using SevenZip.Sdk.Compression.Lzma;
- /// <summary>
- /// Class to pack data into archives supported by 7-Zip.
- /// </summary>
- /// <example>
- /// var compr = new SevenZipCompressor();
- /// compr.CompressDirectory(@"C:\Dir", @"C:\Archive.7z");
- /// </example>
- public sealed partial class SevenZipCompressor
- #if UNMANAGED
- : SevenZipBase
- #endif
- {
- #if UNMANAGED
- #region Fields
- private bool _compressingFilesOnDisk;
- /// <summary>
- /// Gets or sets the archiving compression level.
- /// </summary>
- public CompressionLevel CompressionLevel { get; set; }
- private OutArchiveFormat _archiveFormat = OutArchiveFormat.SevenZip;
- private CompressionMethod _compressionMethod = CompressionMethod.Default;
- /// <summary>
- /// Gets the custom compression parameters - for advanced users only.
- /// </summary>
- public Dictionary<string, string> CustomParameters { get; private set; }
- private long _volumeSize;
- private string _archiveName;
- /// <summary>
- /// Gets or sets the value indicating whether to include empty directories to archives. Default is true.
- /// </summary>
- public bool IncludeEmptyDirectories { get; set; }
- /// <summary>
- /// Gets or sets the value indicating whether to preserve the directory root for CompressDirectory.
- /// </summary>
- public bool PreserveDirectoryRoot { get; set; }
- /// <summary>
- /// Gets or sets the value indicating whether to preserve the directory structure.
- /// </summary>
- public bool DirectoryStructure { get; set; }
- private bool _directoryCompress;
- /// <summary>
- /// Gets or sets the compression mode.
- /// </summary>
- public CompressionMode CompressionMode { get; set; }
- private UpdateData _updateData;
- private uint _oldFilesCount;
- /// <summary>
- /// Gets or sets the value indicating whether to encrypt 7-Zip archive headers.
- /// </summary>
- public bool EncryptHeaders { get; set; }
- /// <summary>
- /// Gets or sets the value indicating whether to compress files only open for writing.
- /// </summary>
- public bool ScanOnlyWritable { get; set; }
- /// <summary>
- /// Gets or sets the encryption method for zip archives.
- /// </summary>
- public ZipEncryptionMethod ZipEncryptionMethod { get; set; }
- /// <summary>
- /// Gets or sets the temporary folder path.
- /// </summary>
- public string TempFolderPath { get; set; }
- /// <summary>
- /// Gets or sets the default archive item name used when an item to be compressed has no name,
- /// for example, when you compress a MemoryStream instance.
- /// </summary>
- public string DefaultItemName { get; set; }
- /// <summary>
- /// Gets or sets the value indicating whether to compress as fast as possible, without calling events.
- /// </summary>
- public bool FastCompression { get; set; }
- #endregion
- #endif
- private static volatile int _lzmaDictionarySize = 1 << 22;
- #if UNMANAGED
- private void CommonInit()
- {
- DirectoryStructure = true;
- IncludeEmptyDirectories = true;
- CompressionLevel = CompressionLevel.Normal;
- CompressionMode = CompressionMode.Create;
- ZipEncryptionMethod = ZipEncryptionMethod.ZipCrypto;
- CustomParameters = new Dictionary<string, string>();
- _updateData = new UpdateData();
- DefaultItemName = "default";
- }
- /// <summary>
- /// Initializes a new instance of the SevenZipCompressor class.
- /// </summary>
- public SevenZipCompressor()
- {
- try
- {
- TempFolderPath = Path.GetTempPath();
- }
- catch (System.Security.SecurityException) // Registry access is not allowed, etc.
- {
- throw new SevenZipCompressionFailedException("Path.GetTempPath() threw a System.Security.SecurityException. You must call SevenZipCompressor constructor overload with your own temporary path.");
- }
- CommonInit();
- }
- /// <summary>
- /// Initializes a new instance of the SevenZipCompressor class.
- /// </summary>
- /// <param name="temporaryPath">Your own temporary path (default is set in the parameterless constructor overload.)</param>
- public SevenZipCompressor(string temporaryPath)
- {
- TempFolderPath = temporaryPath;
- if (!Directory.Exists(TempFolderPath))
- {
- try
- {
- Directory.CreateDirectory(TempFolderPath);
- }
- catch (Exception)
- {
- throw new SevenZipCompressionFailedException("The specified temporary path is invalid.");
- }
- }
- CommonInit();
- }
- #endif
- /// <summary>
- /// Checks if the specified stream supports compression.
- /// </summary>
- /// <param name="stream">The stream to check.</param>
- private static void ValidateStream(Stream stream)
- {
- if (!stream.CanWrite || !stream.CanSeek)
- {
- throw new ArgumentException("The specified stream can not seek or is not writable.", nameof(stream));
- }
- }
- #if UNMANAGED
- #region Private functions
- private IOutArchive MakeOutArchive(IInStream inArchiveStream)
- {
- var inArchive = SevenZipLibraryManager.InArchive(Formats.InForOutFormats[_archiveFormat], this);
- using (var openCallback = GetArchiveOpenCallback())
- {
- ulong checkPos = 1 << 15;
- if (inArchive.Open(inArchiveStream, ref checkPos, openCallback) != (int) OperationResult.Ok)
- {
- if (!ThrowException(null, new SevenZipArchiveException("Can not update the archive: Open() failed.")))
- {
- return null;
- }
- }
- _oldFilesCount = inArchive.GetNumberOfItems();
- }
- return (IOutArchive) inArchive;
- }
- /// <summary>
- /// Guaranties the correct work of the SetCompressionProperties function
- /// </summary>
- /// <param name="method">The compression method to check</param>
- /// <returns>The value indicating whether the specified method is valid for the current ArchiveFormat</returns>
- private bool MethodIsValid(CompressionMethod method)
- {
- if (method == CompressionMethod.Default)
- {
- return true;
- }
- // TODO: Decide what to do with a returned "false" from this method!
- switch (_archiveFormat)
- {
- case OutArchiveFormat.GZip:
- {
- return method == CompressionMethod.Deflate;
- }
- case OutArchiveFormat.BZip2:
- {
- return method == CompressionMethod.BZip2;
- }
- case OutArchiveFormat.SevenZip:
- {
- return method != CompressionMethod.Deflate && method != CompressionMethod.Deflate64;
- }
- case OutArchiveFormat.Tar:
- {
- return method == CompressionMethod.Copy;
- }
- case OutArchiveFormat.Zip:
- {
- return method != CompressionMethod.Lzma2;
- }
- default:
- {
- return true;
- }
- }
- }
- private bool SwitchIsInCustomParameters(string name)
- {
- return CustomParameters.ContainsKey(name);
- }
- /// <summary>
- /// Sets the compression properties
- /// </summary>
- private void SetCompressionProperties()
- {
- switch (_archiveFormat)
- {
- case OutArchiveFormat.Tar:
- {
- break;
- }
- default:
- {
- var setter =
- CompressionMode == CompressionMode.Create && _updateData.FileNamesToModify == null ?
- (ISetProperties) SevenZipLibraryManager.OutArchive(_archiveFormat, this) :
- (ISetProperties) SevenZipLibraryManager.InArchive(Formats.InForOutFormats[_archiveFormat], this);
-
- if (setter == null)
- {
- if (!ThrowException(null, new CompressionFailedException("The specified archive format is unsupported.")))
- {
- return;
- }
- }
- if (_volumeSize > 0 && ArchiveFormat != OutArchiveFormat.SevenZip)
- {
- throw new CompressionFailedException("Unfortunately, the creation of multi-volume non-7Zip archives is not implemented.");
- }
- #region Check for "forbidden" parameters
- if (CustomParameters.ContainsKey("x"))
- {
- if (!ThrowException(null, new CompressionFailedException("Use the \"CompressionLevel\" property instead of the \"x\" parameter.")))
- {
- return;
- }
- }
- if (CustomParameters.ContainsKey("em"))
- {
- if (!ThrowException(null, new CompressionFailedException("Use the \"ZipEncryptionMethod\" property instead of the \"em\" parameter.")))
- {
- return;
- }
- }
- if (CustomParameters.ContainsKey("m"))
- {
- if (!ThrowException(null, new CompressionFailedException("Use the \"CompressionMethod\" property instead of the \"m\" parameter.")))
- {
- return;
- }
- }
- #endregion
- var names = new List<IntPtr>(2 + CustomParameters.Count);
- var values = new List<PropVariant>(2 + CustomParameters.Count);
- #if NET472 || NETSTANDARD2_0
- var sp = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
- sp.Demand();
- #endif
- #region Initialize compression properties
- names.Add(Marshal.StringToBSTR("x"));
- values.Add(new PropVariant());
- if (_compressionMethod != CompressionMethod.Default)
- {
- names.Add(_archiveFormat == OutArchiveFormat.Zip ?
- Marshal.StringToBSTR("m") :
- Marshal.StringToBSTR("0"));
- var pv = new PropVariant
- {
- VarType = VarEnum.VT_BSTR,
- Value = Marshal.StringToBSTR(Formats.MethodNames[_compressionMethod])
- };
- values.Add(pv);
- }
- foreach (var pair in CustomParameters)
- {
- #region Validate parameters against compression method.
- if (_compressionMethod != CompressionMethod.Ppmd && (pair.Key.Equals("mem") || pair.Key.Equals("o")))
- {
- ThrowException(null, new CompressionFailedException($"Parameter \"{pair.Key}\" is only valid with the PPMd compression method."));
- }
- #endregion
- names.Add(Marshal.StringToBSTR(pair.Key));
- var pv = new PropVariant();
- if (pair.Value.All(char.IsDigit))
- {
- pv.VarType = VarEnum.VT_UI4;
- pv.UInt32Value = Convert.ToUInt32(pair.Value, CultureInfo.InvariantCulture);
- }
- else
- {
- pv.VarType = VarEnum.VT_BSTR;
- pv.Value = Marshal.StringToBSTR(pair.Value);
- }
- values.Add(pv);
- }
- #endregion
- #region Set compression level
- var clpv = values[0];
- clpv.VarType = VarEnum.VT_UI4;
- switch (CompressionLevel)
- {
- case CompressionLevel.None:
- {
- clpv.UInt32Value = 0;
- break;
- }
- case CompressionLevel.Fast:
- {
- clpv.UInt32Value = 1;
- break;
- }
- case CompressionLevel.Low:
- {
- clpv.UInt32Value = 3;
- break;
- }
- case CompressionLevel.Normal:
- {
- clpv.UInt32Value = 5;
- break;
- }
- case CompressionLevel.High:
- {
- clpv.UInt32Value = 7;
- break;
- }
- case CompressionLevel.Ultra:
- {
- clpv.UInt32Value = 9;
- break;
- }
- }
- values[0] = clpv;
- #endregion
- #region Encrypt headers
- if (EncryptHeaders && _archiveFormat == OutArchiveFormat.SevenZip && !SwitchIsInCustomParameters("he"))
- {
- names.Add(Marshal.StringToBSTR("he"));
- var tmp = new PropVariant {VarType = VarEnum.VT_BSTR, Value = Marshal.StringToBSTR("on")};
- values.Add(tmp);
- }
- #endregion
- #region Zip Encryption
- if (_archiveFormat == OutArchiveFormat.Zip &&
- ZipEncryptionMethod != ZipEncryptionMethod.ZipCrypto &&
- !SwitchIsInCustomParameters("em"))
- {
- names.Add(Marshal.StringToBSTR("em"));
- var tmp = new PropVariant
- {
- VarType = VarEnum.VT_BSTR,
- Value = Marshal.StringToBSTR(Enum.GetName(typeof(ZipEncryptionMethod), ZipEncryptionMethod))
- };
- values.Add(tmp);
- }
- #endregion
- var namesHandle = GCHandle.Alloc(names.ToArray(), GCHandleType.Pinned);
- var valuesHandle = GCHandle.Alloc(values.ToArray(), GCHandleType.Pinned);
- try
- {
- setter?.SetProperties(namesHandle.AddrOfPinnedObject(), valuesHandle.AddrOfPinnedObject(), names.Count);
- }
- finally
- {
- namesHandle.Free();
- valuesHandle.Free();
- }
- break;
- }
- }
- }
- /// <summary>
- /// Finds the common root of file names
- /// </summary>
- /// <param name="files">Array of file names</param>
- /// <returns>Common root</returns>
- private static int CommonRoot(ICollection<string> files)
- {
- var splitFileNames = new List<string[]>(files.Count);
- splitFileNames.AddRange(files.Select(fn => fn.Split(Path.DirectorySeparatorChar)));
- var minSplitLength = splitFileNames[0].Length - 1;
- if (files.Count > 1)
- {
- for (var i = 1; i < files.Count; i++)
- {
- if (minSplitLength > splitFileNames[i].Length)
- {
- minSplitLength = splitFileNames[i].Length;
- }
- }
- }
- var res = "";
- for (var i = 0; i < minSplitLength; i++)
- {
- var common = true;
- for (var j = 1; j < files.Count; j++)
- {
- if (!(common &= splitFileNames[j - 1][i] == splitFileNames[j][i]))
- {
- break;
- }
- }
- if (common)
- {
- res += splitFileNames[0][i] + Path.DirectorySeparatorChar;
- }
- else
- {
- break;
- }
- }
- return res.Length;
- }
- /// <summary>
- /// Validates the common root
- /// </summary>
- /// <param name="commonRootLength">The length of the common root of the file names.</param>
- /// <param name="files">Array of file names</param>
- private static void CheckCommonRoot(IReadOnlyList<string> files, ref int commonRootLength)
- {
- string commonRoot;
- try
- {
- commonRoot = files[0].Substring(0, commonRootLength);
- }
- catch (ArgumentOutOfRangeException)
- {
- throw new SevenZipInvalidFileNamesException("invalid common root.");
- }
- if (commonRoot.EndsWith(new string(Path.DirectorySeparatorChar, 1), StringComparison.CurrentCulture))
- {
- commonRoot = commonRoot.Substring(0, commonRootLength - 1);
- commonRootLength--;
- }
- if (files.Any(fn => !fn.StartsWith(commonRoot, StringComparison.CurrentCulture)))
- {
- throw new SevenZipInvalidFileNamesException("invalid common root.");
- }
- }
- /// <summary>
- /// Ensures that directory directory is not empty
- /// </summary>
- /// <param name="directory">Directory name</param>
- /// <returns>False if is not empty</returns>
- private static bool RecursiveDirectoryEmptyCheck(string directory)
- {
- var di = new DirectoryInfo(directory);
- if (di.GetFiles().Length > 0)
- {
- return false;
- }
- var empty = true;
- foreach (var cdi in di.GetDirectories())
- {
- empty &= RecursiveDirectoryEmptyCheck(cdi.FullName);
- if (!empty)
- {
- return false;
- }
- }
- return true;
- }
- /// <summary>
- /// Makes special FileInfo array for the archive file table.
- /// </summary>
- /// <param name="files">Array of files to pack.</param>
- /// <param name="commonRootLength">The length of the common root of file names</param>
- /// <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>
- /// <param name="directoryStructure">Preserve directory structure.</param>
- /// <returns>Special FileInfo array for the archive file table.</returns>
- private static FileInfo[] ProduceFileInfoArray(
- IReadOnlyList<string> files, int commonRootLength,
- bool directoryCompress, bool directoryStructure)
- {
- var fis = new List<FileInfo>(files.Count);
- var commonRoot = files[0].Substring(0, commonRootLength);
- if (directoryCompress)
- {
- fis.AddRange(files.Select(fn => new FileInfo(fn)));
- }
- else
- {
- if (!directoryStructure)
- {
- fis.AddRange(from fn in files where !Directory.Exists(fn) select new FileInfo(fn));
- }
- else
- {
- var fns = new List<string>(files.Count);
- CheckCommonRoot(files, ref commonRootLength);
- if (commonRootLength > 0)
- {
- commonRootLength++;
- foreach (var f in files)
- {
- var splitAfn = f.Substring(commonRootLength).Split(Path.DirectorySeparatorChar);
- var cfn = commonRoot;
- foreach (var t in splitAfn)
- {
- cfn += Path.DirectorySeparatorChar + t;
- if (!fns.Contains(cfn))
- {
- fis.Add(new FileInfo(cfn));
- fns.Add(cfn);
- }
- }
- }
- }
- else
- {
- foreach (var f in files)
- {
- var splitAfn = f.Substring(commonRootLength).Split(Path.DirectorySeparatorChar);
- var cfn = splitAfn[0];
- for (var i = 1; i < splitAfn.Length; i++)
- {
- cfn += Path.DirectorySeparatorChar + splitAfn[i];
- if (!fns.Contains(cfn))
- {
- fis.Add(new FileInfo(cfn));
- fns.Add(cfn);
- }
- }
- }
- }
- }
- }
- return fis.ToArray();
- }
- /// <summary>
- /// Recursive function for adding files in directory
- /// </summary>
- /// <param name="directory">Directory directory</param>
- /// <param name="files">List of files</param>
- /// <param name="searchPattern">Search string, such as "*.txt"</param>
- private void AddFilesFromDirectory(string directory, ICollection<string> files, string searchPattern)
- {
- var di = new DirectoryInfo(directory);
- foreach (var fi in di.GetFiles(searchPattern))
- {
- if (!ScanOnlyWritable)
- {
- files.Add(fi.FullName);
- }
- else
- {
- try
- {
- using (fi.OpenWrite())
- {
- }
- files.Add(fi.FullName);
- }
- catch (IOException)
- {
- }
- }
- }
- foreach (var cdi in di.GetDirectories())
- {
- if (IncludeEmptyDirectories)
- {
- files.Add(cdi.FullName);
- }
- AddFilesFromDirectory(cdi.FullName, files, searchPattern);
- }
- }
- #endregion
- #region GetArchiveUpdateCallback overloads
- /// <summary>
- /// Performs the common ArchiveUpdateCallback initialization.
- /// </summary>
- /// <param name="auc">The ArchiveUpdateCallback instance to initialize.</param>
- private void CommonUpdateCallbackInit(ArchiveUpdateCallback auc)
- {
- auc.FileCompressionStarted += FileCompressionStartedEventProxy;
- auc.Compressing += CompressingEventProxy;
- auc.FileCompressionFinished += FileCompressionFinishedEventProxy;
- auc.DefaultItemName = DefaultItemName;
- auc.FastCompression = FastCompression;
- }
- /// <summary>
- /// Performs the common ArchiveUpdateCallback initialization.
- /// </summary>
- /// <param name="auc">The ArchiveUpdateCallback instance to initialize.</param>
- private void CommonUpdateCallbackInitWithFileTime(ArchiveUpdateCallbackWithFileTime auc)
- {
- auc.FileCompressionStarted += FileCompressionStartedEventProxy;
- auc.Compressing += CompressingEventProxy;
- auc.FileCompressionFinished += FileCompressionFinishedEventProxy;
- auc.DefaultItemName = DefaultItemName;
- auc.FastCompression = FastCompression;
- }
- private float GetDictionarySize()
- {
- var dictionarySize = 0.001f;
- switch (_compressionMethod)
- {
- case CompressionMethod.Default:
- case CompressionMethod.Lzma:
- case CompressionMethod.Lzma2:
- {
- switch (CompressionLevel)
- {
- case CompressionLevel.None:
- {
- dictionarySize = 0.001f;
- break;
- }
- case CompressionLevel.Fast:
- {
- dictionarySize = 1.0f / 16 * 7.5f + 4;
- break;
- }
- case CompressionLevel.Low:
- {
- dictionarySize = 7.5f * 11.5f + 4;
- break;
- }
- case CompressionLevel.Normal:
- {
- dictionarySize = 16 * 11.5f + 4;
- break;
- }
- case CompressionLevel.High:
- {
- dictionarySize = 32 * 11.5f + 4;
- break;
- }
- case CompressionLevel.Ultra:
- {
- dictionarySize = 64 * 11.5f + 4;
- break;
- }
- }
- break;
- }
- case CompressionMethod.BZip2:
- {
- switch (CompressionLevel)
- {
- case CompressionLevel.None:
- {
- dictionarySize = 0;
- break;
- }
- case CompressionLevel.Fast:
- {
- dictionarySize = 0.095f;
- break;
- }
- case CompressionLevel.Low:
- {
- dictionarySize = 0.477f;
- break;
- }
- case CompressionLevel.Normal:
- case CompressionLevel.High:
- case CompressionLevel.Ultra:
- {
- dictionarySize = 0.858f;
- break;
- }
- }
- break;
- }
- case CompressionMethod.Deflate:
- case CompressionMethod.Deflate64:
- {
- dictionarySize = 32;
- break;
- }
- case CompressionMethod.Ppmd:
- {
- dictionarySize = 16;
- break;
- }
- }
- return dictionarySize;
- }
- /// <summary>
- /// Produces a new instance of ArchiveUpdateCallback class.
- /// </summary>
- /// <param name="files">Array of FileInfo - files to pack</param>
- /// <param name="rootLength">Length of the common root of file names</param>
- /// <param name="password">The archive password</param>
- /// <returns></returns>
- private ArchiveUpdateCallback GetArchiveUpdateCallback(
- FileInfo[] files, int rootLength, string password)
- {
- SetCompressionProperties();
- var auc = (string.IsNullOrEmpty(password))
- ? new ArchiveUpdateCallback(files, rootLength, this, GetUpdateData(), DirectoryStructure)
- {DictionarySize = GetDictionarySize()}
- : new ArchiveUpdateCallback(files, rootLength, password, this, GetUpdateData(), DirectoryStructure)
- {DictionarySize = GetDictionarySize()};
- CommonUpdateCallbackInit(auc);
- return auc;
- }
- /// <summary>
- /// Produces a new instance of ArchiveUpdateCallback class.
- /// </summary>
- /// <param name="inStream">The archive input stream.</param>
- /// <param name="password">The archive password.</param>
- /// <returns></returns>
- private ArchiveUpdateCallback GetArchiveUpdateCallback(Stream inStream, string password)
- {
- SetCompressionProperties();
- var auc = (string.IsNullOrEmpty(password))
- ? new ArchiveUpdateCallback(inStream, this, GetUpdateData(), DirectoryStructure)
- {DictionarySize = GetDictionarySize()}
- : new ArchiveUpdateCallback(inStream, password, this, GetUpdateData(), DirectoryStructure)
- {DictionarySize = GetDictionarySize()};
- CommonUpdateCallbackInit(auc);
- return auc;
- }
- /// <summary>
- /// Produces a new instance of ArchiveUpdateCallback class.
- /// </summary>
- /// <param name="streamDict">Dictionary<name of the archive entry, stream>.</param>
- /// <param name="password">The archive password</param>
- /// <returns></returns>
- private ArchiveUpdateCallback GetArchiveUpdateCallback(
- IDictionary<string, Stream> streamDict, string password)
- {
- SetCompressionProperties();
- var auc = (string.IsNullOrEmpty(password))
- ? new ArchiveUpdateCallback(streamDict, this, GetUpdateData(), DirectoryStructure)
- {DictionarySize = GetDictionarySize()}
- : new ArchiveUpdateCallback(streamDict, password, this, GetUpdateData(), DirectoryStructure)
- {DictionarySize = GetDictionarySize()};
- CommonUpdateCallbackInit(auc);
- return auc;
- }
- /// <summary>
- /// Produces a new instance of ArchiveUpdateCallback class.
- /// </summary>
- /// <param name="streamDict">Dictionary<name of the archive entry, stream>.</param>
- /// <param name="password">The archive password</param>
- /// <returns></returns>
- private ArchiveUpdateCallbackWithFileTime GetArchiveUpdateCallbackWithFileTime(
- IDictionary<string, CompressStreamEntry> streamDict, string password)
- {
- SetCompressionProperties();
- var auc = (string.IsNullOrEmpty(password))
- ? new ArchiveUpdateCallbackWithFileTime(streamDict, this, GetUpdateData(), DirectoryStructure)
- { DictionarySize = GetDictionarySize() }
- : new ArchiveUpdateCallbackWithFileTime(streamDict, password, this, GetUpdateData(), DirectoryStructure)
- { DictionarySize = GetDictionarySize() };
- CommonUpdateCallbackInitWithFileTime(auc);
- return auc;
- }
- #endregion
- #region Service "Get" functions
- private void FreeCompressionCallback(ArchiveUpdateCallback callback)
- {
- callback.FileCompressionStarted -= FileCompressionStartedEventProxy;
- callback.Compressing -= CompressingEventProxy;
- callback.FileCompressionFinished -= FileCompressionFinishedEventProxy;
- }
- private void FreeCompressionCallbackWithFileTime(ArchiveUpdateCallbackWithFileTime callback)
- {
- callback.FileCompressionStarted -= FileCompressionStartedEventProxy;
- callback.Compressing -= CompressingEventProxy;
- callback.FileCompressionFinished -= FileCompressionFinishedEventProxy;
- }
- private string GetTempArchiveFileName(string archiveName)
- {
- return Path.Combine(TempFolderPath, Path.GetFileName(archiveName) + ".~");
- }
- private FileStream GetArchiveFileStream(string archiveName)
- {
- if ((CompressionMode != CompressionMode.Create || _updateData.FileNamesToModify != null) && !File.Exists(archiveName))
- {
- if (
- !ThrowException(null,
- new CompressionFailedException("file \"" + archiveName + "\" does not exist.")))
- {
- return null;
- }
- }
- return _volumeSize == 0
- ? CompressionMode == CompressionMode.Create && _updateData.FileNamesToModify == null
- ? File.Create(archiveName)
- : File.Create(GetTempArchiveFileName(archiveName))
- : null;
- }
- private void FinalizeUpdate()
- {
- if (_volumeSize == 0 && (CompressionMode != CompressionMode.Create || _updateData.FileNamesToModify != null))
- {
- File.Move(GetTempArchiveFileName(_archiveName), _archiveName);
- }
- }
- private UpdateData GetUpdateData()
- {
- if (_updateData.FileNamesToModify == null)
- {
- var updateData = new UpdateData {Mode = (InternalCompressionMode) ((int) CompressionMode)};
- switch (CompressionMode)
- {
- case CompressionMode.Create:
- {
- updateData.FilesCount = uint.MaxValue;
- break;
- }
- case CompressionMode.Append:
- {
- updateData.FilesCount = _oldFilesCount;
- break;
- }
- }
- return updateData;
- }
- return _updateData;
- }
- private ISequentialOutStream GetOutStream(Stream outStream)
- {
- if (!_compressingFilesOnDisk)
- {
- return new OutStreamWrapper(outStream, false);
- }
- if (_volumeSize == 0 || CompressionMode != CompressionMode.Create || _updateData.FileNamesToModify != null)
- {
- return new OutStreamWrapper(outStream, true);
- }
- return new OutMultiStreamWrapper(_archiveName, _volumeSize);
- }
- private IInStream GetInStream()
- {
- return File.Exists(_archiveName) &&
- (CompressionMode != CompressionMode.Create && _compressingFilesOnDisk ||
- _updateData.FileNamesToModify != null)
- ? new InStreamWrapper(
- new FileStream(_archiveName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite),
- true)
- : null;
- }
- private ArchiveOpenCallback GetArchiveOpenCallback()
- {
- return string.IsNullOrEmpty(Password)
- ? new ArchiveOpenCallback(_archiveName)
- : new ArchiveOpenCallback(_archiveName, Password);
- }
- #endregion
- #region Core public Members
- #region Events
- /// <summary>
- /// Occurs when the next file is going to be packed.
- /// </summary>
- /// <remarks>Occurs when 7-zip engine requests for an input stream for the next file to pack it</remarks>
- public event EventHandler<FileNameEventArgs> FileCompressionStarted;
- /// <summary>
- /// Occurs when the current file was compressed.
- /// </summary>
- public event EventHandler<EventArgs> FileCompressionFinished;
- /// <summary>
- /// Occurs when data are being compressed
- /// </summary>
- /// <remarks>Use this event for accurate progress handling and various ProgressBar.StepBy(e.PercentDelta) routines</remarks>
- public event EventHandler<ProgressEventArgs> Compressing;
- /// <summary>
- /// Occurs when all files information was determined and SevenZipCompressor is about to start to compress them.
- /// </summary>
- /// <remarks>The incoming int value indicates the number of scanned files.</remarks>
- public event EventHandler<IntEventArgs> FilesFound;
- /// <summary>
- /// Occurs when the compression procedure is finished
- /// </summary>
- public event EventHandler<EventArgs> CompressionFinished;
- #region Event proxies
- /// <summary>
- /// Event proxy for FileCompressionStarted.
- /// </summary>
- /// <param name="sender">The sender of the event.</param>
- /// <param name="e">The event arguments.</param>
- private void FileCompressionStartedEventProxy(object sender, FileNameEventArgs e)
- {
- OnEvent(FileCompressionStarted, e, false);
- }
- /// <summary>
- /// Event proxy for FileCompressionFinished.
- /// </summary>
- /// <param name="sender">The sender of the event.</param>
- /// <param name="e">The event arguments.</param>
- private void FileCompressionFinishedEventProxy(object sender, EventArgs e)
- {
- OnEvent(FileCompressionFinished, e, false);
- }
- /// <summary>
- /// Event proxy for Compressing.
- /// </summary>
- /// <param name="sender">The sender of the event.</param>
- /// <param name="e">The event arguments.</param>
- private void CompressingEventProxy(object sender, ProgressEventArgs e)
- {
- OnEvent(Compressing, e, false);
- }
- /// <summary>
- /// Event proxy for FilesFound.
- /// </summary>
- /// <param name="sender">The sender of the event.</param>
- /// <param name="e">The event arguments.</param>
- private void FilesFoundEventProxy(object sender, IntEventArgs e)
- {
- OnEvent(FilesFound, e, false);
- }
- #endregion
- #endregion
- #region Properties
- /// <summary>
- /// Gets or sets the archive format
- /// </summary>
- public OutArchiveFormat ArchiveFormat
- {
- get => _archiveFormat;
- set
- {
- _archiveFormat = value;
- if (!MethodIsValid(_compressionMethod))
- {
- _compressionMethod = CompressionMethod.Default;
- }
- }
- }
- /// <summary>
- /// Gets or sets the compression method
- /// </summary>
- public CompressionMethod CompressionMethod
- {
- get => _compressionMethod;
- set => _compressionMethod = !MethodIsValid(value) ? CompressionMethod.Default : value;
- }
- /// <summary>
- /// Gets or sets the size in bytes of an archive volume (0 for no volumes).
- /// </summary>
- public long VolumeSize
- {
- get => _volumeSize;
- set => _volumeSize = value > 0 ? value : 0;
- }
- #endregion
- #region CompressFiles overloads
- /// <summary>
- /// Packs files into the archive.
- /// </summary>
- /// <param name="fileFullNames">Array of file names to pack.</param>
- /// <param name="archiveName">The archive file name.</param>
- public void CompressFiles(
- string archiveName, params string[] fileFullNames)
- {
- CompressFilesEncrypted(archiveName, string.Empty, fileFullNames);
- }
- /// <summary>
- /// Packs files into the archive.
- /// </summary>
- /// <param name="fileFullNames">Array of file names to pack.</param>
- /// <param name="archiveStream">The archive output stream.
- /// Use CompressFiles(string archiveName ... ) overloads for archiving to disk.</param>
- public void CompressFiles(
- Stream archiveStream, params string[] fileFullNames)
- {
- CompressFilesEncrypted(archiveStream, string.Empty, fileFullNames);
- }
- /// <summary>
- /// Packs files into the archive.
- /// </summary>
- /// <param name="fileFullNames">Array of file names to pack.</param>
- /// <param name="commonRootLength">The length of the common root of the file names.</param>
- /// <param name="archiveName">The archive file name.</param>
- public void CompressFiles(
- string archiveName, int commonRootLength, params string[] fileFullNames)
- {
- CompressFilesEncrypted(archiveName, commonRootLength, string.Empty, fileFullNames);
- }
- /// <summary>
- /// Packs files into the archive.
- /// </summary>
- /// <param name="fileFullNames">Array of file names to pack.</param>
- /// <param name="commonRootLength">The length of the common root of the file names.</param>
- /// <param name="archiveStream">The archive output stream.
- /// Use CompressFiles(string archiveName, ... ) overloads for archiving to disk.</param>
- public void CompressFiles(
- Stream archiveStream, int commonRootLength, params string[] fileFullNames)
- {
- fileFullNames = GetFullFilePaths(fileFullNames);
- CompressFilesEncrypted(archiveStream, commonRootLength, string.Empty, fileFullNames);
- }
- /// <summary>
- /// Packs files into the archive.
- /// </summary>
- /// <param name="fileFullNames">Array of file names to pack.</param>
- /// <param name="archiveName">The archive file name.</param>
- /// <param name="password">The archive password.</param>
- public void CompressFilesEncrypted(
- string archiveName, string password, params string[] fileFullNames)
- {
- fileFullNames = GetFullFilePaths(fileFullNames);
- CompressFilesEncrypted(archiveName, CommonRoot(fileFullNames), password, fileFullNames);
- }
- /// <summary>
- /// Packs files into the archive.
- /// </summary>
- /// <param name="fileFullNames">Array of file names to pack.</param>
- /// <param name="archiveStream">The archive output stream.
- /// Use CompressFiles( ... string archiveName ... ) overloads for archiving to disk.</param>
- /// <param name="password">The archive password.</param>
- public void CompressFilesEncrypted(
- Stream archiveStream, string password, params string[] fileFullNames)
- {
- fileFullNames = GetFullFilePaths(fileFullNames);
- CompressFilesEncrypted(archiveStream, CommonRoot(fileFullNames), password, fileFullNames);
- }
- /// <summary>
- /// Packs files into the archive.
- /// </summary>
- /// <param name="fileFullNames">Array of file names to pack.</param>
- /// <param name="commonRootLength">The length of the common root of the file names.</param>
- /// <param name="archiveName">The archive file name.</param>
- /// <param name="password">The archive password.</param>
- public void CompressFilesEncrypted(string archiveName, int commonRootLength, string password, params string[] fileFullNames)
- {
- _compressingFilesOnDisk = true;
- _archiveName = archiveName;
- using (var fs = GetArchiveFileStream(archiveName))
- {
- if (fs == null && _volumeSize == 0)
- {
- return;
- }
- CompressFilesEncrypted(fs, commonRootLength, password, fileFullNames);
- }
- FinalizeUpdate();
- }
- /// <summary>
- /// Packs files into the archive.
- /// </summary>
- /// <param name="fileFullNames">Array of file names to pack.</param>
- /// <param name="commonRootLength">The length of the common root of the file names.</param>
- /// <param name="archiveStream">The archive output stream.
- /// Use CompressFiles( ... string archiveName ... ) overloads for archiving to disk.</param>
- /// <param name="password">The archive password.</param>
- public void CompressFilesEncrypted(
- Stream archiveStream, int commonRootLength, string password, params string[] fileFullNames)
- {
- ClearExceptions();
- if (fileFullNames.Length > 1 &&
- (_archiveFormat == OutArchiveFormat.BZip2 || _archiveFormat == OutArchiveFormat.GZip ||
- _archiveFormat == OutArchiveFormat.XZ))
- {
- if (!ThrowException(null,
- new CompressionFailedException("Can not compress more than one file in this format.")))
- {
- return;
- }
- }
-
- UpdateCompressorPassword(password);
- if (_volumeSize == 0 || !_compressingFilesOnDisk)
- {
- ValidateStream(archiveStream);
- }
- FileInfo[] files = null;
- try
- {
- files = ProduceFileInfoArray(fileFullNames, commonRootLength, _directoryCompress, DirectoryStructure);
- }
- catch (Exception e)
- {
- if (!ThrowException(null, e))
- {
- return;
- }
- }
- _directoryCompress = false;
- FilesFound?.Invoke(this, new IntEventArgs(fileFullNames.Length));
- try
- {
- ISequentialOutStream sequentialArchiveStream;
- using ((sequentialArchiveStream = GetOutStream(archiveStream)) as IDisposable)
- {
- IInStream inArchiveStream;
- using ((inArchiveStream = GetInStream()) as IDisposable)
- {
- IOutArchive outArchive;
- if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
- {
- SevenZipLibraryManager.LoadLibrary(this, _archiveFormat);
- outArchive = SevenZipLibraryManager.OutArchive(_archiveFormat, this);
- }
- else
- {
- // Create IInArchive, read it and convert to IOutArchive
- SevenZipLibraryManager.LoadLibrary(this, Formats.InForOutFormats[_archiveFormat]);
- if ((outArchive = MakeOutArchive(inArchiveStream)) == null)
- {
- return;
- }
- }
- using (var auc = GetArchiveUpdateCallback(files, commonRootLength, password))
- {
- try
- {
- if (files != null)
- CheckedExecute(
- outArchive.UpdateItems(
- sequentialArchiveStream, (uint) files.Length + _oldFilesCount, auc),
- SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
- }
- finally
- {
- FreeCompressionCallback(auc);
- }
- }
- }
- }
- }
- finally
- {
- if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
- {
- SevenZipLibraryManager.FreeLibrary(this, _archiveFormat);
- }
- else
- {
- SevenZipLibraryManager.FreeLibrary(this, Formats.InForOutFormats[_archiveFormat]);
- File.Delete(_archiveName);
- }
- _compressingFilesOnDisk = false;
- OnEvent(CompressionFinished, EventArgs.Empty, false);
- }
- ThrowUserException();
- }
- #endregion
- #region CompressDirectory overloads
- /// <summary>
- /// Packs all files in the specified directory.
- /// </summary>
- /// <param name="directory">The directory to compress.</param>
- /// <param name="archiveName">The archive file name.</param>
- /// <param name="password">The archive password.</param>
- /// <param name="searchPattern">Search string, such as "*.txt".</param>
- /// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
- public void CompressDirectory(string directory, string archiveName, string password = "", string searchPattern = "*", bool recursion = true)
- {
- _compressingFilesOnDisk = true;
- _archiveName = archiveName;
- using (var fs = GetArchiveFileStream(archiveName))
- {
- if (fs == null && _volumeSize == 0)
- {
- return;
- }
- CompressDirectory(directory, fs, password, searchPattern, recursion);
- }
- FinalizeUpdate();
- }
- /// <summary>
- /// Packs all files in the specified directory.
- /// </summary>
- /// <param name="directory">The directory to compress.</param>
- /// <param name="archiveStream">The archive output stream.
- /// Use CompressDirectory( ... string archiveName ... ) overloads for archiving to disk.</param>
- /// <param name="password">The archive password.</param>
- /// <param name="searchPattern">Search string, such as "*.txt".</param>
- /// <param name="recursion">If true, files will be searched for recursively; otherwise, not.</param>
- public void CompressDirectory(string directory, Stream archiveStream, string password = "", string searchPattern = "*", bool recursion = true)
- {
- var files = new List<string>();
- if (!Directory.Exists(directory))
- {
- throw new ArgumentException("Directory \"" + directory + "\" does not exist!");
- }
- // Get full path, in case this is eg. an SFN path.
- directory = Path.GetFullPath(directory);
- if (RecursiveDirectoryEmptyCheck(directory))
- {
- throw new SevenZipInvalidFileNamesException("the specified directory is empty!");
- }
- if (recursion)
- {
- AddFilesFromDirectory(directory, files, searchPattern);
- }
- else
- {
- files.AddRange((new DirectoryInfo(directory)).GetFiles(searchPattern).Select(fi => fi.FullName));
- }
- var commonRootLength = directory.Length;
- if (directory.EndsWith("\\", StringComparison.OrdinalIgnoreCase))
- {
- directory = directory.Substring(0, directory.Length - 1);
- }
- else
- {
- commonRootLength++;
- }
- if (PreserveDirectoryRoot)
- {
- var upperRoot = Path.GetDirectoryName(directory);
- if (upperRoot != null)
- {
- commonRootLength = upperRoot.Length + (upperRoot.EndsWith("\\", StringComparison.OrdinalIgnoreCase) ? 0 : 1);
- }
- }
- _directoryCompress = true;
- CompressFilesEncrypted(archiveStream, commonRootLength, password, files.ToArray());
- }
- #endregion
- #region CompressFileDictionary overloads
- /// <summary>
- /// Packs the specified file dictionary.
- /// </summary>
- /// <param name="fileDictionary">Dictionary<name of the archive entry, file name>.
- /// If a file name is null, the corresponding archive entry becomes a directory.</param>
- /// <param name="archiveName">The archive file name.</param>
- /// <param name="password">The archive password.</param>
- public void CompressFileDictionary(
- IDictionary<string, string> fileDictionary, string archiveName, string password = "")
- {
- _compressingFilesOnDisk = true;
- _archiveName = archiveName;
- using (var fs = GetArchiveFileStream(archiveName))
- {
- if (fs == null && _volumeSize == 0)
- {
- return;
- }
- CompressFileDictionary(fileDictionary, fs, password);
- }
- FinalizeUpdate();
- }
- /// <summary>
- /// Packs the specified file dictionary.
- /// </summary>
- /// <param name="fileDictionary">Dictionary<name of the archive entry, file name>.
- /// If a file name is null, the corresponding archive entry becomes a directory.</param>
- /// <param name="archiveStream">The archive output stream.
- /// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
- /// <param name="password">The archive password.</param>
- public void CompressFileDictionary(IDictionary<string, string> fileDictionary, Stream archiveStream, string password = "")
- {
- var streamDict = new Dictionary<string, Stream>(fileDictionary.Count);
- foreach (var pair in fileDictionary)
- {
- if (pair.Value == null)
- {
- streamDict.Add(pair.Key, null);
- }
- else
- {
- if (!File.Exists(pair.Value))
- {
- throw new CompressionFailedException(
- "The file corresponding to the archive entry \"" + pair.Key + "\" does not exist.");
- }
- streamDict.Add(
- pair.Key,
- new FileStream(pair.Value, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
- }
- }
- //The created streams will be automatically disposed inside.
- CompressStreamDictionary(streamDict, archiveStream, password);
- }
- #endregion
- #region CompressStreamDictionary overloads
- /// <summary>
- /// Packs the specified stream dictionary.
- /// </summary>
- /// <param name="streamDictionary">Dictionary<name of the archive entry, stream>.
- /// If a stream is null, the corresponding string becomes a directory name.</param>
- /// <param name="archiveName">The archive file name.</param>
- /// <param name="password">The archive password.</param>
- public void CompressStreamDictionary(IDictionary<string, Stream> streamDictionary, string archiveName, string password = "")
- {
- _compressingFilesOnDisk = true;
- _archiveName = archiveName;
- using (var fs = GetArchiveFileStream(archiveName))
- {
- if (fs == null && _volumeSize == 0)
- {
- return;
- }
- CompressStreamDictionary(streamDictionary, fs, password);
- }
- FinalizeUpdate();
- }
- /// <summary>
- /// Packs the specified stream dictionary.
- /// </summary>
- /// <param name="streamDictionary">Dictionary<name of the archive entry, stream>.
- /// If a stream is null, the corresponding string becomes a directory name.</param>
- /// <param name="archiveStream">The archive output stream.
- /// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
- /// <param name="password">The archive password.</param>
- public void CompressStreamDictionary(IDictionary<string, Stream> streamDictionary, Stream archiveStream, string password = "")
- {
- ClearExceptions();
- if (streamDictionary.Count > 1 &&
- (_archiveFormat == OutArchiveFormat.BZip2 || _archiveFormat == OutArchiveFormat.GZip ||
- _archiveFormat == OutArchiveFormat.XZ))
- {
- if (!ThrowException(null,
- new CompressionFailedException("Can not compress more than one file/stream in this format.")))
- {
- return;
- }
- }
- if (_volumeSize == 0 || !_compressingFilesOnDisk)
- {
- ValidateStream(archiveStream);
- }
-
- UpdateCompressorPassword(password);
-
- if (streamDictionary.Where(
- pair => pair.Value != null && (!pair.Value.CanSeek || !pair.Value.CanRead)).Any(
- pair => !ThrowException(null,
- new ArgumentException(
- $"The specified stream dictionary contains an invalid stream corresponding to the archive entry \"{pair.Key}\".",
- nameof(streamDictionary)))))
- {
- return;
- }
- try
- {
- ISequentialOutStream sequentialArchiveStream;
- using ((sequentialArchiveStream = GetOutStream(archiveStream)) as IDisposable)
- {
- IInStream inArchiveStream;
- using ((inArchiveStream = GetInStream()) as IDisposable)
- {
- IOutArchive outArchive;
- if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
- {
- SevenZipLibraryManager.LoadLibrary(this, _archiveFormat);
- outArchive = SevenZipLibraryManager.OutArchive(_archiveFormat, this);
- }
- else
- {
- // Create IInArchive, read it and convert to IOutArchive
- SevenZipLibraryManager.LoadLibrary(
- this, Formats.InForOutFormats[_archiveFormat]);
- if ((outArchive = MakeOutArchive(inArchiveStream)) == null)
- {
- return;
- }
- }
- using (var auc = GetArchiveUpdateCallback(streamDictionary, password))
- {
- try
- {
- CheckedExecute(outArchive.UpdateItems(sequentialArchiveStream,
- (uint) streamDictionary.Count + _oldFilesCount, auc),
- SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
- }
- finally
- {
- FreeCompressionCallback(auc);
- }
- }
- }
- }
- }
- finally
- {
- if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
- {
- SevenZipLibraryManager.FreeLibrary(this, _archiveFormat);
- }
- else
- {
- SevenZipLibraryManager.FreeLibrary(this, Formats.InForOutFormats[_archiveFormat]);
- File.Delete(_archiveName);
- }
- _compressingFilesOnDisk = false;
- OnEvent(CompressionFinished, EventArgs.Empty, false);
- }
- ThrowUserException();
- }
- /// <summary>
- /// Packs the specified stream dictionary.
- /// </summary>
- /// <param name="streamDictionary">Dictionary<name of the archive entry, stream>.
- /// If a stream is null, the corresponding string becomes a directory name.</param>
- /// <param name="archiveStream">The archive output stream.
- /// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
- /// <param name="password">The archive password.</param>
- public void CompressStreamDictionaryWithFileTime(IDictionary<string, CompressStreamEntry> streamDictionary, Stream archiveStream, string password = "")
- {
- ClearExceptions();
- if (streamDictionary.Count > 1 &&
- (_archiveFormat == OutArchiveFormat.BZip2 || _archiveFormat == OutArchiveFormat.GZip ||
- _archiveFormat == OutArchiveFormat.XZ))
- {
- if (!ThrowException(null,
- new CompressionFailedException("Can not compress more than one file/stream in this format.")))
- {
- return;
- }
- }
- if (_volumeSize == 0 || !_compressingFilesOnDisk)
- {
- ValidateStream(archiveStream);
- }
- UpdateCompressorPassword(password);
- if (streamDictionary.Where(
- pair => pair.Value.Stream != null && (!pair.Value.Stream.CanSeek || !pair.Value.Stream.CanRead)).Any(
- pair => !ThrowException(null,
- new ArgumentException(
- $"The specified stream dictionary contains an invalid stream corresponding to the archive entry \"{pair.Key}\".",
- nameof(streamDictionary)))))
- {
- return;
- }
- try
- {
- ISequentialOutStream sequentialArchiveStream;
- using ((sequentialArchiveStream = GetOutStream(archiveStream)) as IDisposable)
- {
- IInStream inArchiveStream;
- using ((inArchiveStream = GetInStream()) as IDisposable)
- {
- IOutArchive outArchive;
- if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
- {
- SevenZipLibraryManager.LoadLibrary(this, _archiveFormat);
- outArchive = SevenZipLibraryManager.OutArchive(_archiveFormat, this);
- }
- else
- {
- // Create IInArchive, read it and convert to IOutArchive
- SevenZipLibraryManager.LoadLibrary(
- this, Formats.InForOutFormats[_archiveFormat]);
- if ((outArchive = MakeOutArchive(inArchiveStream)) == null)
- {
- return;
- }
- }
- using (var auc = GetArchiveUpdateCallbackWithFileTime(streamDictionary, password))
- {
- try
- {
- CheckedExecute(outArchive.UpdateItems(sequentialArchiveStream,
- (uint)streamDictionary.Count + _oldFilesCount, auc),
- SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
- }
- finally
- {
- FreeCompressionCallbackWithFileTime(auc);
- }
- }
- }
- }
- }
- finally
- {
- if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
- {
- SevenZipLibraryManager.FreeLibrary(this, _archiveFormat);
- }
- else
- {
- SevenZipLibraryManager.FreeLibrary(this, Formats.InForOutFormats[_archiveFormat]);
- File.Delete(_archiveName);
- }
- _compressingFilesOnDisk = false;
- OnEvent(CompressionFinished, EventArgs.Empty, false);
- }
- ThrowUserException();
- }
- #endregion
- #region CompressStream overloads
- /// <summary>
- /// Compresses the specified stream.
- /// </summary>
- /// <param name="inStream">The source uncompressed stream.</param>
- /// <param name="outStream">The destination compressed stream.</param>
- /// <param name="password">The archive password.</param>
- /// <exception cref="ArgumentException">ArgumentException: at least one of the specified streams is invalid.</exception>
- public void CompressStream(Stream inStream, Stream outStream, string password = "")
- {
- ClearExceptions();
- if (!inStream.CanSeek || !inStream.CanRead || !outStream.CanWrite)
- {
- if (!ThrowException(null, new ArgumentException("The specified streams are invalid.")))
- {
- return;
- }
- }
- try
- {
- SevenZipLibraryManager.LoadLibrary(this, _archiveFormat);
- ISequentialOutStream sequentialArchiveStream;
- using ((sequentialArchiveStream = GetOutStream(outStream)) as IDisposable)
- {
- using (var auc = GetArchiveUpdateCallback(inStream, password))
- {
- try
- {
- CheckedExecute(
- SevenZipLibraryManager.OutArchive(_archiveFormat, this).UpdateItems(
- sequentialArchiveStream, 1, auc),
- SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
- }
- finally
- {
- FreeCompressionCallback(auc);
- }
- }
- }
- }
- finally
- {
- SevenZipLibraryManager.FreeLibrary(this, _archiveFormat);
- OnEvent(CompressionFinished, EventArgs.Empty, false);
- }
- ThrowUserException();
- }
- #endregion
- #region ModifyArchive overloads
- /// <summary>
- /// Modifies the existing archive (renames files or deletes them).
- /// </summary>
- /// <param name="archiveName">The archive file name.</param>
- /// <param name="newFileNames">New file names. Null value to delete the corresponding index.</param>
- /// <param name="password">The archive password.</param>
- public void ModifyArchive(string archiveName, IDictionary<int, string> newFileNames, string password = "")
- {
- ClearExceptions();
- if (!SevenZipLibraryManager.ModifyCapable)
- {
- throw new SevenZipLibraryException("The specified 7zip native library does not support this method.");
- }
- if (!File.Exists(archiveName))
- {
- if (!ThrowException(null,
- new ArgumentException("The specified archive does not exist.", nameof(archiveName))))
- {
- return;
- }
- }
- if (newFileNames == null || newFileNames.Count == 0)
- {
- if (!ThrowException(null, new ArgumentException("Invalid new file names.", nameof(newFileNames))))
- {
- return;
- }
- }
- UpdateCompressorPassword(password);
- try
- {
- using (var extractor = new SevenZipExtractor(archiveName, password))
- {
- _updateData = new UpdateData();
- var archiveData = new ArchiveFileInfo[extractor.ArchiveFileData.Count];
- extractor.ArchiveFileData.CopyTo(archiveData, 0);
- _updateData.ArchiveFileData = new List<ArchiveFileInfo>(archiveData);
- }
- _updateData.FileNamesToModify = newFileNames;
- _updateData.Mode = InternalCompressionMode.Modify;
- }
- catch (SevenZipException e)
- {
- if (!ThrowException(null, e))
- {
- return;
- }
- }
- try
- {
- ISequentialOutStream sequentialArchiveStream;
- _compressingFilesOnDisk = true;
- using ((sequentialArchiveStream = GetOutStream(GetArchiveFileStream(archiveName))) as IDisposable)
- {
- IInStream inArchiveStream;
- _archiveName = archiveName;
- using ((inArchiveStream = GetInStream()) as IDisposable)
- {
- IOutArchive outArchive;
- // Create IInArchive, read it and convert to IOutArchive
- SevenZipLibraryManager.LoadLibrary(
- this, Formats.InForOutFormats[_archiveFormat]);
- if ((outArchive = MakeOutArchive(inArchiveStream)) == null)
- {
- return;
- }
- using (var auc = GetArchiveUpdateCallback(null, 0, password))
- {
- uint deleteCount = 0;
- if (_updateData.FileNamesToModify != null)
- {
- deleteCount = (uint) _updateData.FileNamesToModify.Sum(
- pairDeleted => pairDeleted.Value == null ? 1 : 0);
- }
- try
- {
- CheckedExecute(
- outArchive.UpdateItems(
- sequentialArchiveStream, _oldFilesCount - deleteCount, auc),
- SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
- }
- finally
- {
- FreeCompressionCallback(auc);
- }
- }
- }
- }
- }
- finally
- {
- SevenZipLibraryManager.FreeLibrary(this, Formats.InForOutFormats[_archiveFormat]);
- File.Delete(archiveName);
- FinalizeUpdate();
- _compressingFilesOnDisk = false;
- _updateData.FileNamesToModify = null;
- _updateData.ArchiveFileData = null;
- OnEvent(CompressionFinished, EventArgs.Empty, false);
- }
- ThrowUserException();
- }
- #endregion
- #endregion
- #endif
- /// <summary>
- /// Gets or sets the dictionary size for the managed LZMA algorithm.
- /// </summary>
- public static int LzmaDictionarySize
- {
- get => _lzmaDictionarySize;
- set => _lzmaDictionarySize = value;
- }
- internal static void WriteLzmaProperties(Encoder encoder)
- {
- #region LZMA properties definition
- CoderPropId[] propIDs =
- {
- CoderPropId.DictionarySize,
- CoderPropId.PosStateBits,
- CoderPropId.LitContextBits,
- CoderPropId.LitPosBits,
- CoderPropId.Algorithm,
- CoderPropId.NumFastBytes,
- CoderPropId.MatchFinder,
- CoderPropId.EndMarker
- };
- object[] properties =
- {
- _lzmaDictionarySize,
- 2,
- 3,
- 0,
- 2,
- 256,
- "bt4",
- false
- };
- #endregion
- encoder.SetCoderProperties(propIDs, properties);
- }
- /// <summary>
- /// Compresses the specified stream with LZMA algorithm (C# inside)
- /// </summary>
- /// <param name="inStream">The source uncompressed stream</param>
- /// <param name="outStream">The destination compressed stream</param>
- /// <param name="inLength">The length of uncompressed data (null for inStream.Length)</param>
- /// <param name="codeProgressEvent">The event for handling the code progress</param>
- public static void CompressStream(Stream inStream, Stream outStream, int? inLength,
- EventHandler<ProgressEventArgs> codeProgressEvent)
- {
- if (!inStream.CanRead || !outStream.CanWrite)
- {
- throw new ArgumentException("The specified streams are invalid.");
- }
- var encoder = new Encoder();
- WriteLzmaProperties(encoder);
- encoder.WriteCoderProperties(outStream);
- var streamSize = inLength ?? inStream.Length;
- for (var i = 0; i < 8; i++)
- {
- outStream.WriteByte((byte) (streamSize >> (8 * i)));
- }
- encoder.Code(inStream, outStream, -1, -1, new LzmaProgressCallback(streamSize, codeProgressEvent));
- }
- /// <summary>
- /// Compresses byte array with LZMA algorithm (C# inside)
- /// </summary>
- /// <param name="data">Byte array to compress</param>
- /// <returns>Compressed byte array</returns>
- public static byte[] CompressBytes(byte[] data)
- {
- using (var inStream = new MemoryStream(data))
- {
- using (var outStream = new MemoryStream())
- {
- var encoder = new Encoder();
- WriteLzmaProperties(encoder);
- encoder.WriteCoderProperties(outStream);
- var streamSize = inStream.Length;
- for (var i = 0; i < 8; i++)
- {
- outStream.WriteByte((byte) (streamSize >> (8 * i)));
- }
- encoder.Code(inStream, outStream, -1, -1, null);
- return outStream.ToArray();
- }
- }
- }
- /// <summary>
- /// Ensures an array of file names is the full path to that file.
- /// </summary>
- /// <param name="fileFullNames">Array of file names.</param>
- /// <returns>Array of file names with full paths.</returns>
- private static string[] GetFullFilePaths(IEnumerable<string> fileFullNames)
- {
- return fileFullNames.Select(Path.GetFullPath).ToArray();
- }
-
- /// <summary>
- /// Check and update password in SevenZipCompressor
- /// </summary>
- /// <param name="password">The password to use.</param>
- private void UpdateCompressorPassword(string password)
- {
- if (!string.IsNullOrEmpty(password) && string.IsNullOrEmpty(Password))
- {
- // When modifying an encrypted archive, Password is not set in the SevenZipCompressor.
- Password = password;
- }
- }
- }
- }
|