public static void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, IProgress <double> progress)
        {
            using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName))
            {
                double totalBytes   = archive.Entries.Sum(e => e.Length);
                long   currentBytes = 0;

                foreach (ZipArchiveEntry entry in archive.Entries)
                {
                    string fileName = Path.Combine(destinationDirectoryName, entry.FullName);

                    Directory.CreateDirectory(Path.GetDirectoryName(fileName));
                    using (Stream inputStream = entry.Open())
                        using (Stream outputStream = File.OpenWrite(fileName))
                        {
                            Stream progressStream = new StreamWithProgress(outputStream, null,
                                                                           new BasicProgress <int>(i =>
                            {
                                currentBytes += i;
                                progress.Report(currentBytes / totalBytes);
                            }));

                            inputStream.CopyTo(progressStream);
                        }

                    File.SetLastWriteTime(fileName, entry.LastWriteTime.LocalDateTime);
                }
            }
        }
        public static void CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName, IProgress <double> progress)
        {
            sourceDirectoryName = Path.GetFullPath(sourceDirectoryName);

            FileInfo[] sourceFiles =
                new DirectoryInfo(sourceDirectoryName).GetFiles("*", SearchOption.AllDirectories);
            double totalBytes   = sourceFiles.Sum(f => f.Length);
            long   currentBytes = 0;

            using (ZipArchive archive = ZipFile.Open(destinationArchiveFileName, ZipArchiveMode.Create))
            {
                foreach (FileInfo file in sourceFiles)
                {
                    // NOTE: naive method to get sub-path from file name, relative to
                    // input directory. Production code should be more robust than this.
                    // Either use Path class or similar to parse directory separators and
                    // reconstruct output file name, or change this entire method to be
                    // recursive so that it can follow the sub-directories and include them
                    // in the entry name as they are processed.
                    string          entryName = file.FullName.Substring(sourceDirectoryName.Length + 1);
                    ZipArchiveEntry entry     = archive.CreateEntry(entryName);

                    entry.LastWriteTime = file.LastWriteTime;

                    using (Stream inputStream = File.OpenRead(file.FullName))
                        using (Stream outputStream = entry.Open())
                        {
                            Stream progressStream = new StreamWithProgress(inputStream,
                                                                           new BasicProgress <int>(i =>
                            {
                                currentBytes += i;
                                progress.Report(currentBytes / totalBytes);
                            }), null);

                            progressStream.CopyTo(outputStream);
                        }
                }
            }
        }