private static void UnpackArchiveStructure(ManagedLzma.SevenZip.FileModel.ArchivedFolder folder, string targetDirectory) { if (folder.Items.IsEmpty) { // Empty folders need to be created manually since the unpacking code doesn't try to write into it. Directory.CreateDirectory(targetDirectory); } else { foreach (var item in folder.Items) { var file = item as ManagedLzma.SevenZip.FileModel.ArchivedFile; if (file != null) { // Files without content are not iterated during normal unpacking so we need to create them manually. if (file.Stream.IsUndefined) { System.Diagnostics.Debug.Assert(file.Length == 0); // If the file has no content then its length should be zero, otherwise something is wrong. var filename = Path.Combine(targetDirectory, file.Name); using (var stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Delete)) { // Nothing to do, FileMode.Create already truncates the file on opening. } SetFileAttributes(filename, file); } } var subfolder = item as ManagedLzma.SevenZip.FileModel.ArchivedFolder; if (subfolder != null) UnpackArchiveStructure(subfolder, Path.Combine(targetDirectory, subfolder.Name)); } } }
private static void SetFileAttributes(string path, ManagedLzma.SevenZip.FileModel.ArchivedFile file) { if (file.Attributes.HasValue) { // When calling File.SetAttributes we need to preserve existing attributes which are not part of the archive var attr = File.GetAttributes(path); const FileAttributes kAttrMask = ArchivedAttributesExtensions.FileAttributeMask; attr = (attr & ~kAttrMask) | (file.Attributes.Value.ToFileAttributes() & kAttrMask); File.SetAttributes(path, attr); } }
private static void UnpackArchive(string archiveFileName, string targetDirectory, ManagedLzma.PasswordStorage password) { if (!File.Exists(archiveFileName)) throw new FileNotFoundException("Archive not found.", archiveFileName); // Ensure that the target directory exists. Directory.CreateDirectory(targetDirectory); using (var archiveStream = new FileStream(archiveFileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)) { var archiveMetadataReader = new ManagedLzma.SevenZip.FileModel.ArchiveFileModelMetadataReader(); var archiveFileModel = archiveMetadataReader.ReadMetadata(archiveStream, password); var archiveMetadata = archiveFileModel.Metadata; for (int sectionIndex = 0; sectionIndex < archiveMetadata.DecoderSections.Length; sectionIndex++) { var sectionReader = new ManagedLzma.SevenZip.Reader.DecodedSectionReader(archiveStream, archiveMetadata, sectionIndex, password); var sectionFiles = archiveFileModel.GetFilesInSection(sectionIndex); // The section reader is constructed from metadata, if the counts do not match there must be a bug somewhere. System.Diagnostics.Debug.Assert(sectionFiles.Count == sectionReader.StreamCount); // The section reader iterates over all files in the section. NextStream advances the iterator. for (; sectionReader.CurrentStreamIndex < sectionReader.StreamCount; sectionReader.NextStream()) { var fileMetadata = sectionFiles[sectionReader.CurrentStreamIndex]; // The ArchiveFileModelMetadataReader we used above processes special marker nodes and resolves some conflicts // in the archive metadata so we don't have to deal with them. In these cases there will be no file metadata // produced and we should skip the stream. If you want to process these cases manually you should use a different // MetadataReader subclass or write your own subclass. if (fileMetadata == null) continue; // These asserts need to hold, otherwise there's a bug in the mapping the metadata reader produced. System.Diagnostics.Debug.Assert(fileMetadata.Stream.SectionIndex == sectionIndex); System.Diagnostics.Debug.Assert(fileMetadata.Stream.StreamIndex == sectionReader.CurrentStreamIndex); // Ensure that the target directory is created. var filename = Path.Combine(targetDirectory, fileMetadata.FullName); Directory.CreateDirectory(Path.GetDirectoryName(filename)); // NOTE: you can have two using-statements here if you want to be explicit about it, but disposing the // stream provided by the section reader is not mandatory, it is owned by the the section reader // and will be auto-closed when moving to the next stream or when disposing the section reader. using (var stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Delete)) sectionReader.OpenStream().CopyTo(stream); SetFileAttributes(filename, fileMetadata); } } // Create empty files and empty directories. UnpackArchiveStructure(archiveFileModel.RootFolder, targetDirectory); } }