# MetadataExtractor, a .NET parser for the metadata blocks inside media files

> MetadataExtractor reads Exif, IPTC, XMP, ICC, JFIF and Photoshop metadata out of image, movie and audio files, and returns them as typed directories of tags rather than a flat key value bag. It is a managed library with no platform dependency, and it dropped its oldest target frameworks during 2024 and 2025.

**drewnoakes/metadata-extractor-dotnet** — Extracts Exif, IPTC, XMP, ICC and other metadata from image, video and audio files

- Repository: https://github.com/drewnoakes/metadata-extractor-dotnet
- Stars: 1,079 · Forks: 190
- Language: C#
- License: NOASSERTION
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/drewnoakes-metadata-extractor-dotnet

## Metadata lives in blocks, and the library models them as directories

The problem the library solves is reading the metadata blocks embedded in media files rather than a handful of properties that a platform happens to expose. A single image can carry several kinds of metadata at once, and the project lists the formats it understands as Exif, IPTC, XMP, JFIF and JFXX, ICC Profiles and Photoshop data. The design decision is in the return type. Reading a file gives you a sequence of directories, and each directory holds tags, and each tag has a name and a human readable description. The documented example of that output shows an Exif SubIFD directory carrying the exposure time and the f-number, an Exif IFD0 directory carrying the camera make and model, and an IPTC directory carrying the credit and the city. The structure mirrors how the metadata is actually organised in the file, which is why a consumer can tell which block a value came from instead of guessing.

## Reading one value means casting to the directory type

Reading everything is a foreach over directories and tags. Reading one value means narrowing the sequence to the directory type you want, because the tag identifiers are defined per format.

```csharp
var subIfdDirectory = directories.OfType<ExifSubIfdDirectory>().FirstOrDefault();
var dateTime = subIfdDirectory?.GetDescription(ExifDirectoryBase.TagDateTime);
```

The type is ExifSubIfdDirectory, the lookup is GetDescription, and the identifier is a constant on ExifDirectoryBase. The call is null tolerant, since the directory may not be present in a file that has no Exif SubIFD block, and the null conditional operator handles that case without a branch. The alternative to this cast and filter pattern is to scan every tag of every directory by description string, which works and which loses the type information that makes the library useful. For a file with several blocks that carry similarly named fields, the difference between a typed lookup and a string search is the difference between a correct answer and a plausible one.

## Formats cover raw camera files, movies and audio

The file type list is long and the structure of it is informative. For images there is AVIF, BMP, DNG, EPS, GIF, HEIF and HEIC, ICO, JPEG and JFIF, Netpbm, PCX, PNG, PSD, TGA, TIFF and BigTIFF, and WebP. Under that sits camera raw, which is the part that separates this from a general purpose tag reader, with 3FR for Hasselblad, ARW for Sony, CRW, CR2, CR3 and CRX for Canon, GPR for GoPro, KDC for Kodak, NEF for Nikon, ORF for Olympus, PEF for Pentax, RAF for Fujifilm, RW2 for Panasonic, RWL for Leica and SRW for Samsung. Movies are covered with AVCI, AVI, MOV and MP4, and audio with WAV and MP3. Beyond the container formats, the library decodes the camera specific makernote data for Agfa, Apple, Canon, Casio, DJI, Epson, FLIR, Fujifilm, Kodak, Kyocera, Leica, Minolta, Nikon, Olympus, Panasonic, Pentax, Reconyx, Ricoh, Samsung, Sanyo, Sigma/Foveon and Sony. That list is the real differentiator, since makernote blocks are where a camera stores its own proprietary fields.

## Adding it to a project

The library is distributed as a NuGet package named MetadataExtractor, and the project documents three ways to take it. A project file reference is the one most teams use.

```xml
<ItemGroup>
    <PackageReference Include="MetadataExtractor" Version="2.9.1" />
</ItemGroup>
```

Inside Visual Studio, the Package Manager Console takes the same package by name.

```bash
PM> Install-Package MetadataExtractor
```

Or the NuGet Package Manager can find it by searching for MetadataExtractor. The first call in your code is a single static method that takes a path and returns the directory sequence.

```csharp
IEnumerable<Directory> directories = ImageMetadataReader.ReadMetadata(imagePath);
```

Printing everything is two nested loops over directories and tags, writing the directory name, the tag name and the tag description. There is no configuration, no scanning directory and no database, which is what makes the library usable as a building block inside a larger pipeline rather than as an application.

## One package, three target frameworks, and a shrinking tail

The package ships all supported targets in a single NuGet package. The current set is net8.0, netstandard2.0 and netstandard2.1. The net8.0 build implements .NET 8 and includes NativeAOT support, which is the one that matters if you are compiling to a native executable. netstandard2.0 covers .NET Framework and .NET Core consumers, and netstandard2.1 uses newer APIs where they exist. The history of what was removed is documented, and it is worth reading if you maintain an older application. A PCL build was supported until version 1.5.3 and covered Silverlight 5, Windows 8, Windows Phone 8.1 and Windows Phone Silverlight 8, with the note that those builds could not support file system metadata because of restricted IO APIs. A netstandard1.3 build and a net462 build were supported until version 2.8.1, and netstandard1.3 was dropped in mid 2025 in response to the Microsoft advisory NETSDK1215. A net3.5 build also went away in 2.8.1, in early 2024, to allow use of newer and more efficient .NET APIs.

## The repository is a library plus the tools around it

The top level listing shows a solution with more than the library in it. MetadataExtractor/ is the library itself, and MetadataExtractor.Tests/ holds its tests, with a separate MetadataExtractor.Samples/ directory for runnable examples and a MetadataExtractor.Benchmarks/ project for measurements. Two command line tools sit alongside, MetadataExtractor.Tools.FileProcessor/ for processing files and MetadataExtractor.Tools.JpegSegmentExtractor/ for pulling apart JPEG segments, and there is a MetadataExtractor.PowerShell/ project for scripting from a shell. Build configuration is centralised, with Directory.Build.props, Directory.Build.targets and Directory.Packages.props handling shared settings and package versions across the solution, which is the usual way to keep a multi-project repository consistent. The solution file is now MetadataExtractor.slnx, the newer XML format, and the tree also carries a strong name key file, a spelling dictionary, a wiki directory and docs/. A .gitmodules file and a .git-blame-ignore-revs file complete the picture of a repository that takes code hygiene seriously.

## What it does not do, and where the licence question sits

Two boundaries are worth stating plainly. The library reads metadata; nothing in the project documentation describes writing it, so a tool that needs to stamp or strip fields is looking at a different project. And the value of the output depends on the format being covered, since a file format that is absent from the list will simply produce fewer directories rather than an error, and a makernote from an unlisted camera will come back as undecoded bytes. On the release side, the tags show 2.9.1 on 2026-03-17, 2.9.2 on 2026-03-18 and 2.9.3 on 2026-04-08, and the last push to the default branch was on 2026-10-19, so the branch continues to move after the newest tag. The licence is the item to resolve before shipping. The repository carries a LICENSE file that GitHub could not classify, which means the terms are written in a custom document rather than a standard permissive grant that a scanner will recognise. That is a reading task for your legal team, not a technical one, and nothing here is legal advice.

## Conclusion

MetadataExtractor fits a.NET service that has to normalise camera and media metadata into its own model, whether that is a digital asset pipeline, an archive tool or a photo catalogue, because it returns typed directories you can filter rather than strings you have to parse. It does not fit a team that needs to write metadata back, since this is a read oriented library, and it does not fit a project on a runtime outside net8.0, netstandard2.0 and netstandard2.1, since older targets such as net462 and net3.5 were removed in version 2.8.1. Verify three things first: that the file formats you care about are in the supported list, including the camera raw variants for your own cameras, that the makernote data for your makes is decoded, and what the LICENSE file permits, since GitHub could not classify it.

## FAQ

### What does MetadataExtractor do?

It is a .NET library for reading metadata from image, movie and audio files, understanding Exif, IPTC, XMP, JFIF and JFXX, ICC Profiles and Photoshop data. Reading a file returns a sequence of directories, each holding tags with a name and a description.

### How do I install MetadataExtractor in a .NET project?

Add a PackageReference for MetadataExtractor to the project file, or run Install-Package MetadataExtractor in the Visual Studio Package Manager Console, or search for it in the NuGet Package Manager. The example in the project documentation uses version 2.9.1.

### Which camera raw formats and cameras does MetadataExtractor support?

Camera raw support includes 3FR, ARW, CRW, CR2, CR3, CRX, GPR, KDC, NEF, ORF, PEF, RAF, RW2, RWL and SRW, and makernote data is decoded for 22 manufacturers including Canon, Nikon, Sony, Fujifilm, Olympus, Panasonic, Pentax and Leica.

### Which .NET versions does MetadataExtractor target?

The current package targets net8.0, netstandard2.0 and netstandard2.1 in a single NuGet package, and the net8.0 build includes NativeAOT support. Older netstandard1.3, net462 and net3.5 targets were supported until version 2.8.1.

## Sources

- [drewnoakes/metadata-extractor-dotnet on GitHub](https://github.com/drewnoakes/metadata-extractor-dotnet)
- [Issues](https://github.com/drewnoakes/metadata-extractor-dotnet/issues)
- [README](https://github.com/drewnoakes/metadata-extractor-dotnet/blob/main/README.md)
- [Releases](https://github.com/drewnoakes/metadata-extractor-dotnet/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/drewnoakes-metadata-extractor-dotnet
