---
title: "Extract files from an ISO in C#"
url: https://recursiveai.org/onearchive/cookbook/extract-iso-files/
description: "List and extract regular files from an ISO 9660 image in C# with OneArchive, including Joliet (Unicode file names) and Rock Ridge name selection and a runnable example."
version: "1.2.0"
---

# Extract files from an ISO in C#

**OneArchive** is a free, open-source archive library for .NET from Recursive AI. MIT or Apache-2.0, no native dependencies.

[NuGet](https://www.nuget.org/packages/RecursiveAI.OneArchive) · [GitHub](https://github.com/recursive-ai-oss/onearchive-csharp) · [Docs](https://recursiveai.org/onearchive/docs/) · [Download example (.zip)](https://recursiveai.org/onearchive/cookbook/extract-iso-files/extract-iso-files.zip)

Extract regular files from ISO 9660 without mounting it. `sample.iso` is in the zip.

```shell
dotnet add package RecursiveAI.OneArchive
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

```csharp
using RecursiveAI.OneArchive;
Archive.Extract("sample.iso", "output",
    new ArchiveExtractOptions {
        Filter = e =>
            e.Kind == ArchiveEntryKind.File
    });
Console.WriteLine("Extracted to output");
```

```text
Extracted to output
```

Unzip [the example](https://recursiveai.org/onearchive/cookbook/extract-iso-files/extract-iso-files.zip), 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.

```csharp
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:

```text
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.

```csharp
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);
```

```text
docs/hello.txt
```

## Related recipes

- [Create a zip file](https://recursiveai.org/onearchive/cookbook/create-zip/)
- [Extract a tar.gz file](https://recursiveai.org/onearchive/cookbook/extract-tar-gz/)
- [Extract a 7z archive](https://recursiveai.org/onearchive/cookbook/extract-7z/)

## 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:

```csharp
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](https://recursiveai.org/onearchive/docs/iso/) 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](https://recursiveai.org/onearchive/docs/archive/) 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.
