Extract regular files from ISO 9660 without mounting it. sample.iso is in the zip.
dotnet add package RecursiveAI.OneArchiveRequires .NET 10 (net10.0); .NET 9 and earlier aren’t supported.
using RecursiveAI.OneArchive; Archive.Extract("sample.iso", "output", new ArchiveExtractOptions { Filter = e => e.Kind == ArchiveEntryKind.File }); Console.WriteLine("Extracted to output");
Extracted to outputUnzip the example, then dotnet run.
The full program
The program writes hello.txt containing Hello from ISO! followed by a newline, then builds sample.iso with that file at docs/hello.txt. The included ISO contains only this generated text.
using RecursiveAI.OneArchive; // Create a tiny ISO containing only our own text. File.WriteAllText("hello.txt", "Hello from ISO!\n"); Archive.Create("sample.iso") .Add("hello.txt", "docs/hello.txt").Save(); using (var archive = Archive.Open("sample.iso")) { foreach (var entry in archive.Entries) if (entry.Kind == ArchiveEntryKind.File) Console.WriteLine($"{entry.Name}: {entry.Size} bytes"); } // A new destination on every run avoids overwriting any existing files. string destination = Path.Combine("extracted", Guid.NewGuid().ToString("N")); Archive.Extract("sample.iso", destination, new ArchiveExtractOptions { Filter = entry => entry.Kind == ArchiveEntryKind.File }); Console.Write(File.ReadAllText(Path.Combine(destination, "docs", "hello.txt")));
dotnet run prints:
docs/hello.txt: 16 bytes Hello from ISO!
Existing destination files are refused by default; extracting a zisofs (compressed Rock Ridge files) entry raises ArchiveUnsupportedException.
Variations
List Joliet (Unicode file names)
Run this against the included sample.iso. Joliet is the supplementary name hierarchy used here instead of the automatic choice.
using RecursiveAI.OneArchive; using RecursiveAI.OneArchive.Iso; var options = new ArchiveOpenOptions { Iso = new IsoReaderOptions { Tree = IsoTree.Joliet } }; using var archive = Archive.Open("sample.iso", options); foreach (var entry in archive.Entries) if (entry.Kind == ArchiveEntryKind.File) Console.WriteLine(entry.Name);
docs/hello.txtRelated recipes
The short answer
Use Archive.Extract on the ISO file, with an ArchiveExtractOptions.Filter that selects ArchiveEntryKind.File. It copies regular files to the destination directory without mounting the image.
The full program also creates that sample from its own text, lists regular file names and sizes, then extracts to a new destination on every run. It reads docs/hello.txt from disk afterward, so the final output is extracted content, not just a directory listing.
For your own ISO, replace the Archive.Extract call in the snippet at the top with this line, using your image’s path:
Archive.Extract("your-image.iso", "output", new ArchiveExtractOptions { Filter = e => e.Kind == ArchiveEntryKind.File });Can you extract files from an ISO?
Yes, for the ISO 9660 structures supported by this reader. Archive.Open exposes entries for inspection; Archive.Extract handles destination paths and writes selected content. Listing an entry does not require extracting it. You can inspect a file’s name, kind and uncompressed size before deciding whether to select it.
This is file extraction, not optical-drive access or boot-image execution. Boot records are listed only. The reader requires a logical block size of 2048 bytes; another value raises ArchiveUnsupportedException. Rock Ridge zisofs entries can be listed, but extracting their compressed data is unsupported and raises the same exception. See ISO reader behavior for the supported structures.
How do I extract an ISO to a folder?
Pass the target folder to Archive.Extract, as in the snippet at the top. Keep the regular-file filter when you only want files: it excludes directory entries, symbolic links and special entries from the selection. Parent directories needed for selected files are created during extraction.
Use the extractor rather than joining an untrusted entry name directly to a destination path. Readers report names as stored; the shared extractor is the layer that checks path traversal, link escapes and other unsafe writes. Existing destination files are refused by default. A failed extraction can leave earlier extracted files behind, so use a fresh directory when you want a distinct result per attempt. The full program does this without deleting any existing destination.
Skipped entries still count toward archive limits. A filter narrows what gets written, not what the parser must inspect. Keep the default limits unless you have a specific, trusted input that needs different bounds; the archive options describe them.
How do I preserve ISO file names in C#?
The automatic namespace selection uses the primary hierarchy when Rock Ridge is active, otherwise Joliet when present, otherwise the primary hierarchy. Rock Ridge carries additional Unix-style names and metadata; Joliet is a supplementary hierarchy with Unicode names. The variation selects Joliet explicitly for listing instead of relying on the default.
IsoEntry.Name comes from the chosen name source. IsoEntry.IsoPath retains the identifier path of the hierarchy being read. This distinction matters if you compare entries with a tool that selected a different hierarchy: different displayed names do not necessarily mean different payloads. Choose the same reader options for inspection and extraction when your application relies on a particular path spelling. Rock Ridge applies only to the primary hierarchy, not the Joliet tree.