Home

Awesome

ExifReader

ExifReader is a JavaScript library that parses image files and extracts the metadata. It can also extract an embedded thumbnail. It can be used either in a browser or from Node. Supports JPEG, TIFF, PNG, HEIC, WebP, and GIF files with Exif, IPTC, XMP, ICC, and MPF metadata (depending on file type).

ExifReader is highly and easily configurable and the resulting bundle can be as small as ~4 KiB (gzipped) if you're only interested in a few tags (e.g. date and/or GPS values).

ExifReader supports module formats ESM, AMD, CommonJS, and globals and can therefore easily be used from Webpack, RequireJS, Browserify, Node etc.

You can try it out on the examples site.

Support table

File typeExifIPTCXMPICCMPFPhotoshopCanonThumbnailImage details
JPEGyesyesyesyesyessome*some**yesyes
TIFFyesyesyesyes???some*some**N/AN/A
PNGyesyesyesyes??????some**noyes
HEIC/HEIFyesnoyesyes??????some**yesno
AVIFyesnoyesyes??????some**yesno
WebPyesnoyesyes??????some**yesyes
GIFN/AN/AN/AN/AN/AN/Asome**N/Ayes

If you're missing something that you think should be supported, file an issue with an attached example image and I'll see what I can do.

Notes for exif-js users

If you come here from the popular but now dead exif-js package, please let me know if you're missing anything from it and I will try to help you. Some notes:

Table of Contents

  1. Support
  2. Installation
  3. Usage
  4. Configure a Custom Build
  5. Notes
  6. Client/Browser Support
  7. Examples
  8. Tips
  9. Known Limitations
  10. Contributing
  11. Code of Conduct
  12. License
  13. Changelog

Support

Monetary support is not necessary for me to continue working on this, but in case you like this library and want to support its development you are very welcome to click the button below. You can also use GitHub's sponsor feature on the right-hand side on the repository's main page.

<a href="https://www.buymeacoffee.com/mattiasw" target="_blank"> <img src="https://cdn.buymeacoffee.com/buttons/v2/default-violet.png" alt="Buy me a coffee" width="181" height="50"> </a>

Installation

Easiest is through npm or Bower:

npm install exifreader --save
bower install exifreader --save

If you want to clone the git repository instead:

git clone git@github.com:mattiasw/ExifReader.git
cd ExifReader
npm install

After that, the transpiled, concatenated and minified ES5 file will be in the dist folder together with a sourcemap file.

Type definitions

Type definitions for TypeScript are included in the package. I'm not perfect in maintaining those so if you're missing any definitions for tags or something else, a pull-request would be very welcome.

Usage

Importing

NOTE: See React Native instructions below.

ES module syntax:

import ExifReader from 'exifreader';

NOTE: TypeScript/Angular seems to sometimes have problems when using the default export. If you're seeing issues, use this syntax instead:

import * as ExifReader from 'exifreader';

CommonJS/Node modules:

const ExifReader = require('exifreader');

AMD modules:

requirejs(['/path/to/exif-reader.js'], function (ExifReader) {
    ...
});

script tag:

<script src="/path/to/exif-reader.js"></script>

Loading tags

There are two ways to load the tags. Either have ExifReader do the loading of the image file, or load the file yourself first and pass in the file buffer. The main difference is that the first one is asynchronous and the second one is synchronous unless specified.

Let ExifReader load the file (asynchronous API)

const tags = await ExifReader.load(file);
const imageDate = tags['DateTimeOriginal'].description;
const unprocessedTagValue = tags['DateTimeOriginal'].value;

Where file is one of

Load the file yourself (synchronous API)

const tags = ExifReader.load(fileBuffer);

Where fileBuffer is one of

See the examples site for more directions on how to use the library.

Asynchronous tags

Some tags need to be parsed asynchronously. Currently this is the case for some PNG tags, more specifically compressed tags in zTXt, iTXt, and iCCP chunks. To enable this, either use the asynchronous API mentioned above or pass in async: true in the options parameter:

const tags = await ExifReader.load(file);
// or
const tags = await ExifReader.load(fileBuffer, {async: true});

For the compressed tags to work, the environment needs to support the Compression Streams API.

The reason for having an option to enable this is to not break backwards compatibility. This will probably be the default in the next major version.

Using React Native

Import ExifReader like this:

import ExifReader from './node_modules/exifreader/src/exif-reader.js';

Make sure to update the path to point to where your node_modules is located.

For local files on the device you need to load the file yourself first, then pass in the buffer to ExifReader. Here is a template from user @hungdev:

import RNFS from 'react-native-fs';
import {decode} from 'base64-arraybuffer';
import ExifReader from 'exifreader';

const b64Buffer = await RNFS.readFile('YOUR IMAGE URI', 'base64') // Where the URI looks like this: "file:///path/to/image/IMG_0123.HEIC"
const fileBuffer = decode(b64Buffer)
const tags = ExifReader.load(fileBuffer, {expanded: true});

If you're having trouble getting the GPS location, see this comment and thread and the GPS section below for more details.

Grouping

By default, Exif, IPTC and XMP tags are grouped together. This means that if e.g. Orientation exists in both Exif and XMP, the first value (Exif) will be overwritten by the second (XMP). If you need to separate between these values, pass in an options object with the property expanded set to true:

const tags = ExifReader.load(fileBuffer, {expanded: true});

Read only part of file

If you only want to read part of the image file you can use the length option:

const tags = await ExifReader.load(filename, {length: 128 * 1024});

This will load only the first 128 KiB of the file. This could be useful if you know the metadata is located at the beginning of the file. Just be aware that it's common for the metadata to be spread out over a larger area so please try it out on your set of files to know if it's suitable for your situation.

Note that this option only works when ExifReader handles the loading of the file. If e.g. a JavaScript File object from a form file field is passed into ExifReader the whole file will already have been loaded into memory and it's too late. More specifically the length option will work for 1. local files when running through Node.js, and 2. remote files when passing a URL. For the latter, if doing this through a web browser, make sure the remote server is either on the same origin (domain) as your script or that the server is passing correct CORS headers, specifically allowing the Range header.

Unknown tags

Tags that are unknown, either because they have been excluded by making a custom build or they are yet to be added into ExifReader, are by default not included in the output. If you need to see them there is an option that can be passed in:

const tags = ExifReader.load(fileBuffer, {includeUnknown: true});

If you discover an unknown tag that should be handled by ExifReader, please reach out by filing an issue.

GPS

If expanded: true is specified in the options, there will be a gps group. This group currently contains Latitude, Longitude, and Altitude which will be negative for values that are south of the equator, west of the IRM, or below sealevel. These are often more convenient values for regular use. For some elaboration or if you need the original values, see Notes below.

If you're having trouble getting the GPS values on Android (and possibly also IOS) and are using a type="file" input to upload the image, make sure you are not setting accept="image/*" on the input element. Apparently setting the accept attribute to this value will strip the GPS values.

Using the thumbnail

The thumbnail and its details will be accessible through tags['Thumbnail']. There is information about e.g. width and height, and the thumbnail image data is stored in tags['Thumbnail'].image.

How you use it is going to depend on your environment. For a web browser you can either use the raw byte data in tags['Thumbnail'].image and use it the way you want, or you can use the helper property tags['Thumbnail'].base64 that is a base64 representation of the image. It can be used for a data URI like this:

const tags = ExifReader.load(fileBuffer);
imageElement.src = 'data:image/jpg;base64,' + tags['Thumbnail'].base64;

If you're using node, you can store it as a new file like this:

const fs = require('fs');
const tags = ExifReader.load(fileBuffer);
fs.writeFileSync('/path/to/new/thumbnail.jpg', Buffer.from(tags['Thumbnail'].image));

See the examples site for more details.

Optimizing build size

The most important step will be to use a custom build so please do that.

If you are using Webpack 4 or lower and are only targeting web browsers, make sure to add this to your Webpack config (probably the webpack.config.js file):

    node: {
        Buffer: false
    }

Buffer is only used in Node.js but if Webpack sees a reference to it it will include a Buffer shim for browsers. This configuration will stop Webpack from doing that. Webpack 5 does this automatically.

Configure a Custom Build

Configuring a custom build can reduce the bundle size significantly.

NOTE 1: This functionality is in beta but should work fine. Please file an issue if you're having problems or ideas on how to make it better.

NOTE 2: This only changes the built file (exifreader/dist/exif-reader.js), not the source code. That means it's not possible to use the ES module (from the src folder) or any tree shaking to get the benefit of a custom build. Tree shaking will actually have close to no effect at all here so don't rely on it.

This is for npm and yarn users that use the built file. To specify what functionality you want you can either use include pattern (start with an empty set and include) or exclude pattern (start with full functionality and exclude). If an include pattern is set, excludes will not be used.

For Exif and IPTC it's also possible to specify which tags you're interested in. Those tag groups have huge dictionaries of tags and you may not be interested in all of them. (Note that it's not possible to specify tags to exclude.)

The configuration is added to your project's package.json file.

Example 1: Only include JPEG files and Exif tags (this makes the bundle almost half the size of the full one (non-gzipped)):

"exifreader": {
    "include": {
        "jpeg": true,
        "exif": true
    }
}

Example 2: Only include TIFF files, and the Exif DateTime tag and the GPS tags (resulting bundle will be ~19 % of a gzipped full build):

"exifreader": {
    "include": {
        "tiff": true,
        "exif": [
            "DateTime",
            "GPSLatitude",
            "GPSLatitudeRef",
            "GPSLongitude",
            "GPSLongitudeRef",
            "GPSAltitude",
            "GPSAltitudeRef"
        ]
    }
}

Example 3: Exclude XMP tags:

"exifreader": {
    "exclude": {
        "xmp": true
    }
}

Then, if you didn't install ExifReader yet, run npm install exifreader. Otherwise you have to rebuild the library:

npm rebuild exifreader

With yarn 2+:

From my experience, you need a node_modules folder for the rebuild command to work with yarn 2+. If you don't have it, run yarn config set nodeLinker node-modules and then run yarn. Then you can try to rebuild:

yarn rebuild exifreader

With yarn 1:

yarn add exifreader

After that the new bundle is here: node_modules/exifreader/dist/exif-reader.js

If you are using vite, you will need to clear the dependency cache after a rebuild.

If you're using the include pattern config, remember to include everything you want to use. If you want xmp and don't specify any file types, you will get "Invalid image format", and if you specify jpeg but don't mention any tag types no tags will be found.

Possible modules to include or exclude:

ModuleDescription
jpegJPEG images.
tiffTIFF images.
pngPNG images.
heicHEIC/HEIF images.
webpWebP images.
gifGIF images.
fileJPEG file details: image width, height etc.
jfifJFIF details in JPEG files: resolution, thumbnail etc.
png_filePNG file details: image width, height etc.
exifRegular Exif tags. If excluded, will also exclude mpf, photoshop and thumbnail. For TIFF files, excluding this will also exclude IPTC, XMP, and ICC.
iptcIPTC tags.
xmpXMP tags.
iccICC color profile tags.
mpfMulti-picture Format tags.
photoshopPhotoshop tags.
maker_notesProprietary camera maker tags. Needs exif.
thumbnailThumbnail image. Needs exif.

Notes

Client/Browser Support

The library makes use of the DataView API which is supported in Chrome 9+, Firefox 15+, Internet Explorer 10+, Edge, Safari 5.1+, Opera 12.1+. For Node.js at least version 10 is required if you want to parse XMP tags, otherwise earlier versions will also work.

Examples

Full HTML example pages and a Node.js example are located on the examples site.

Tips

Known Limitations

Contributing

Questions, bug reports, suggestions, and pull requests are very much welcome. If you've been using another Exif package, you probably have some good insights on what's missing in this one. See CONTRIBUTING.md for more info.

Code of Conduct

This project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.

License

ExifReader uses the Mozilla Public License 2.0 (MPL-2.0). In short that means you can use this library in your project (open- or closed-source) as long as you mention the use of ExifReader and make any changes to ExifReader code available if you would to distribute your project. But please read the full license text to make sure your specific case is covered.

Changelog

A selection of notable changes.