/// <summary>Asynchronously deletes all the <b>ZipArchiveEntries</b> specified from the zip file.</summary>
        public async Task DeleteFromZipArchiveAsync(IEnumerable <string> paths, string zipFileName)
        {
            using (var memoryStream = new MemoryStream(await FileAsync.ReadAllBytesAsync(zipFileName)))
            {
                using (var zip = new ZipArchive(memoryStream, ZipArchiveMode.Update))
                {
                    foreach (var entry in zip.Entries.Where(e => paths.Contains(e.FullName)).ToArray())
                    {
                        if (entry != null)
                        {
                            try { entry.Delete(); }

                            catch (Exception ex)
                            {
                                string entryDescription = entry.FullName.IndexOfAny(XBRLConstants.DelimiterChars) == -1 ?
                                                          entry.FullName :
                                                          entry.FullName.Substring(entry.FullName.LastIndexOfAny(XBRLConstants.DelimiterChars) + 1);

                                OnError(new ZipErrorEventArgs(
                                            string.Format("Error deleting '{0}' from '{1}'. {2}.",
                                                          entryDescription,
                                                          Path.GetFileName(zipFileName),
                                                          ex.Message)));
                            }
                        }
                    }
                }
                await FileAsync.WriteAllBytesAsync(zipFileName, memoryStream.ToArray());
            }
        }
        /// <summary>Save a file (or files) to zip asynchronously, using asynchronous IO.</summary>
        /// <param name="paths">The files and/or directories to create a zip file.</param>
        /// <param name="zipFileName">The path of the zip file to create.</param>
        /// <param name="filesToExclude">(Optional) Any file names that should be excluded.</param>
        /// <returns>A Task that represents completion of the method.</returns>
        public async Task CreateZipArchiveAsync(IEnumerable <string> paths, string zipFileName, params string[] filesToExclude)
        {
            var files = await BuildFileCollectionAsync(paths, filesToExclude);

            using (var stream = new FileStream(zipFileName, FileMode.Create, FileAccess.ReadWrite, FileShare.None, XBRLConstants.LargeBufferSize, true))
            {
                // ZipArchiveMode.Update mode is required here to allow setting entries' last write time, after creating and writing them.
                using (var zip = new ZipArchive(stream, ZipArchiveMode.Update))
                {
                    var count = 0;
                    var total = files.Count();

                    foreach (var kvp in files)
                    {
                        try
                        {
                            using (var memoryStream = new MemoryStream(await FileAsync.ReadAllBytesAsync(kvp.Key)))
                            {
                                if (memoryStream.Length > 0)
                                {
                                    ZipArchiveEntry entry;

                                    using (var zipStream = (entry = zip.CreateEntry(kvp.Value, CompressionLevel.Optimal)).Open())
                                    {
                                        await memoryStream.CopyToAsync(zipStream, XBRLConstants.LargeBufferSize);
                                    }

                                    var fileInfo = new FileInfo(kvp.Key);
                                    entry.LastWriteTime = new DateTimeOffset(fileInfo.LastWriteTime);

                                    OnProgress(new ZipProgressEventArgs(kvp.Key, kvp.Value, ++count, total, ZipAction.Add));
                                }
                                else
                                {
                                    total--;
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            OnError(new ZipErrorEventArgs(string.Format("Error adding '{0}'. {1}.", Path.GetFileName(kvp.Key), ex.Message)));
                        }
                    }
                }
            }
        }
        /// <summary>Add a file (or files) to a zip asynchronously, using asynchronous IO.</summary>
        /// <remarks>This is exactly the same as the <see cref="CreateZipArchiveAsync"/> method,
        /// except that it updates an existing zip file.</remarks>
        /// <param name="paths">The files and/or directories to add to a zip file.</param>
        /// <param name="zipFileName">The path of the zip file to update.</param>
        /// <param name="filesToExclude">(Optional) Any file names that should be excluded.</param>
        /// <returns>A Task that represents completion of the method.</returns>
        public async Task UpdateZipArchiveAsync(IEnumerable <string> paths, string zipFileName, params string[] filesToExclude)
        {
            var files = await BuildFileCollectionAsync(paths, filesToExclude);

            /* Note the FileShare parameter needs to be FileShare.Read here. Otherwise, if the zip file
             * is already open in another process, the FileStream constructor will throw an exception. */
            using (var stream = new FileStream(zipFileName, FileMode.Open, FileAccess.ReadWrite, FileShare.Read, XBRLConstants.LargeBufferSize, true))
            {
                using (var zip = new ZipArchive(stream, ZipArchiveMode.Update))
                {
                    var count = 0;

                    foreach (var kvp in files)
                    {
                        try
                        {
                            using (var memoryStream = new MemoryStream(await FileAsync.ReadAllBytesAsync(kvp.Key)))
                            {
                                if (memoryStream.Length > 0)
                                {
                                    ZipArchiveEntry entry;

                                    using (var zipStream = (entry = zip.CreateOrOpenEntry(kvp.Value)).Open())
                                    {
                                        await memoryStream.CopyToAsync(zipStream, XBRLConstants.LargeBufferSize);
                                    }

                                    var fileInfo = new FileInfo(kvp.Key);
                                    entry.LastWriteTime = new DateTimeOffset(fileInfo.LastWriteTime);

                                    OnProgress(new ZipProgressEventArgs(kvp.Key, kvp.Value, ++count, files.Count(), ZipAction.Add));
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            OnError(new ZipErrorEventArgs(string.Format("Error updating '{0}' with '{1}'. {2}.", Path.GetFileName(zipFileName), Path.GetFileName(kvp.Key), ex.Message)));
                        }
                    }
                }
            }
        }