/// <summary> /// Create file provider that re-opens archive. /// </summary> /// <param name="archiveOpener"></param> /// <param name="entryName">Entry name of the whole package</param> /// <param name="hintPath">(optional) archive name "folder/document.txt.gz", entry name is extracted by removing the folder (separator '/') and last extension.</param> /// <param name="dateTime">Date time for folder entries</param> /// <exception cref="IOException">On I/O error</exception> /// <exception cref="PackageException.LoadError">on file format error</exception> public GZipFileProvider(Func <GZipArchive> archiveOpener, string entryName, string hintPath = null, DateTimeOffset?dateTime = default) : base(hintPath, dateTime) { if (archiveOpener == null) { throw new ArgumentNullException(nameof(archiveOpener)); } // Place holder for the uncompressed length value long[] length = new long[1]; // Convert archiveOpener to streamOpener Stream streamOpener() { IArchive archive = archiveOpener(); try { // Search entry IArchiveEntry entry = archive.Entries.First(); // Not found if (entry == null) { archive.Dispose(); return(null); } // Open stream Stream s = entry.OpenEntryStream(); // Attach the disposing of the archive to the stream. return(new GZipStreamFix(s, archive, null, length[0])); } catch (Exception) when(CloseDisposable(archive)) { // Never goes here return(null); } } // Create stream provider this.streamProvider = new StreamOpener(streamOpener, entryName, belatedDisposeList); // Open once to read entries. using (var archive = archiveOpener()) { // Read first entry IArchiveEntry entry = archive.Entries.First(); // Get length using (var s = entry.OpenEntryStream()) length[0] = CalculateLength(s); // File entry var fileEntry = new Lexical.FileProvider.Common.ArchiveFileEntry(streamProvider, entryName, entryName, length[0], dateTime ?? entry.LastModifiedTime ?? DateTime.MinValue); // Put it in directory this.root.files[entryName] = fileEntry; } }
/// <summary> /// Create file provider that reads one entry from <paramref name="archive"/>. /// </summary> /// <param name="archive"></param> /// <param name="entryName">Entry name of the whole package</param> /// <param name="hintPath">(optional) archive name "folder/document.txt.gz", entry name is extracted by removing the folder (separator '/') and last extension.</param> /// <param name="dateTime">(optional) Date time for folder entries</param> public GZipFileProvider(GZipArchive archive, string entryName, string hintPath = null, DateTimeOffset?dateTime = null) : base(hintPath, dateTime) { this.streamProvider = new Lexical.FileProvider.SharpCompress.Internal.ArchiveStreamProvider(archive ?? throw new ArgumentNullException(nameof(archive)), belatedDisposeList); IArchiveEntry entry = archive.Entries.First(); long length; using (Stream s = entry.OpenEntryStream()) length = CalculateLength(s); var fileEntry = new Lexical.FileProvider.Common.ArchiveFileEntry(streamProvider, entry.Key, entryName, length, dateTime ?? entry.LastModifiedTime ?? DateTime.MinValue); this.root.files[entryName] = fileEntry; }
/// <summary> /// Search file from the read-only archive index. /// </summary> /// <param name="path"></param> /// <returns></returns> public virtual IFileInfo GetFileInfo(string path) { if (IsDisposing) { throw new ObjectDisposedException(GetType().FullName); } if (path == null) { path = ""; } string canonizedPath = CanonizePath(path); ArchiveFileEntry zipFile = root.GetFile(canonizedPath); return((IFileInfo)zipFile ?? new NotFoundFileInfo(path)); }
/// <summary> /// Add <paramref name="archiveEntries"/> into tree structure. /// </summary> /// <param name="root"></param> /// <param name="archiveEntries"></param> /// <param name="streamProvider">stream provider for files</param> /// <param name="convertBackslashesToSlashes">if true converts '\' to '/'</param> /// <returns>this</returns> protected virtual Lexical.FileProvider.Common.ArchiveDirectoryEntry AddArchiveEntries(Lexical.FileProvider.Common.ArchiveDirectoryEntry root, IEnumerable <IArchiveEntry> archiveEntries, Lexical.FileProvider.Common.IStreamProvider streamProvider, bool convertBackslashesToSlashes) { foreach (IArchiveEntry entry in archiveEntries) { string path = convertBackslashesToSlashes ? entry.Key.Replace('\\', '/') : entry.Key; // Is entry a file if (!entry.IsDirectory) { // Split to filename and path int slashIx = path.LastIndexOf('/'); string filename = path.Substring(slashIx + 1); string dirPath = slashIx < 0 ? "" : path.Substring(0, slashIx); // Create file entry Lexical.FileProvider.Common.ArchiveFileEntry fileEntry = new Lexical.FileProvider.Common.ArchiveFileEntry(streamProvider, entry.Key, filename, entry.Size, entry.LastModifiedTime ?? DateTime.MinValue); // Create dir Lexical.FileProvider.Common.ArchiveDirectoryEntry dir = root.GetOrCreateDirectory(dirPath); // Add to dir dir.files[filename] = fileEntry; } else { // Create dir var dir = root.GetOrCreateDirectory(path); if (entry.LastModifiedTime != null) { dir.LastModified = (DateTime)entry.LastModifiedTime; } } } // Return the whole tree return(root); }
/// <summary> /// Find file /// </summary> /// <param name="canonizedPath">file path</param> /// <returns>file or null</returns> public ArchiveFileEntry GetFile(string canonizedPath) { if (canonizedPath == null) { throw new ArgumentNullException(nameof(canonizedPath)); } if (canonizedPath == "") { return(null); } // Find slash int slashIX = canonizedPath.IndexOf('/'); // No slash, return direct file if (slashIX < 0) { ArchiveFileEntry result = null; files.TryGetValue(canonizedPath, out result); return(result); } // Got slash, find local dir string localDirectoryName = canonizedPath.Substring(0, slashIX); ArchiveDirectoryEntry localDirectory = null; if (!directories.TryGetValue(localDirectoryName, out localDirectory)) { return(null); } // Use recursion for the rest the path string restOfThePath = canonizedPath.Substring(slashIX + 1); return(localDirectory.GetFile(restOfThePath)); }