exifr

repository·master·Indexed 23 days ago

https://github.com/mikekovarik/exifr

A fast and versatile isomorphic JavaScript library for reading EXIF, XMP, and other metadata from image formats including JPEG, TIFF, PNG, and HEIC. It works in both Browser and Node.js environments and is designed for efficiency by reading only necessary bytes via a chunked reader. The library provides specialized methods for extracting GPS coordinates, orientation, rotation, and embedded thumbnails, and offers multiple bundle sizes (full, lite, mini, core) to balance features and file size.

Tokens
7.4K
Snippets
17
Records
36
Agent score
77%

What's inside exifr

  1. Choose the right exifr bundle

    master

    Exifr is distributed in different bundles to balance features and file size. Choose the one that best fits your use case:

    • full: Contains everything. Intended for use in Node.js.
    • lite: Reads JPEG and HEIC. Parses TIFF/EXIF and XMP. Recommended for browsers.
    • mini: Stripped down to basics. Parses most useful TIFF/EXIF from JPEGs. Has no tag dictionaries. Recommended for browsers.
    • core: The modular core (advanced usage).
  2. Customize translation dictionaries (Keys, Values, and Revivers)

    master

    Exifr uses dictionaries to translate numeric EXIF enums into human-readable strings. You can customize these to change how keys, values, or data types are processed.

    • Key dict: Translates numeric codes to string names (e.g., 0x0110 $\rightarrow$ Model).
    • Value dict: Translates enum values to descriptions (e.g., 3 $\rightarrow$ 'Rotate 180').
    • Reviver: Modifies the value further (e.g., converting a date string into a Date instance).

    Dictionaries are organized by segment (e.g., 'exif', 'gps', 'ifd0').

    // Modify single tag's 0xa409 (Saturation) translation
    import exifr from 'exifr'
    let exifKeys   = exifr.tagKeys.get('exif')
    let exifValues = exifr.tagValues.get('exif')
    exifKeys.set(0xa409, 'Saturation')
    exifValues.set(0xa409, {
      0: 'Normal',
      1: 'Low',
      2: 'High'
    })
  3. Optimize exifr performance for large batches

    master

    To maximize performance when processing many files, follow these patterns:

    1. Use options.pick: Only request the specific tags you need. This allows exifr to stop reading as soon as the last requested tag is found.
    2. Disable options.ifd0: If you don't need Image block data, set ifd0: false. Note that exif: true or gps: true still requires parsing the pointers within IFD0, but setting this to false prevents reading the whole block.
    3. Use specialized APIs: Use exifr.gps(file) or exifr.orientation(file) instead of the generic parse() method for faster, fine-tuned extraction.
    4. Cache the options object: Do not inline the options object in loops. Reusing the same object allows exifr to use an internal WeakMap to find the existing Options instance instead of re-instantiating it.
  4. Use exifr in a Web Worker

    master

    To avoid blocking the main thread during heavy parsing, you can run exifr inside a Web Worker.

    When using postMessage, it is recommended to use Transferable Objects (like ArrayBuffer) to improve performance by transferring ownership of the memory rather than copying it.

    // Main thread
    let worker = new Worker('./worker.js')
    worker.postMessage('../test/IMG_20180725_163423.jpg')
    worker.onmessage = e => console.log(e.data)
    
    // Tip: Use Transferable Objects with ArrayBuffer
    worker.postMessage(arrayBuffer, [arrayBuffer])
    
    // worker.js
    importScripts('./node_modules/exifr/dist/lite.umd.js')
    self.onmessage = async e => postMessage(await exifr.parse(e.data))
  5. Use exifr in the browser via UMD or ESM

    master

    You can integrate exifr into browser applications using either the UMD or ESM distributions found in dist/.

    UMD (Universal Module Definition)

    Load the script via a <script> tag. The library will be available on the window.exifr object.

    ESM (ES Modules)

    Import the library directly within a <script type="module"> block.

    <!-- UMD Example -->
    <img src="./myimage.jpg">
    <script src="./node_modules/exifr/dist/lite.umd.js"></script>
    <script>
      let img = document.querySelector('img')
      window.exifr.parse(img).then(exif => console.log('Exposure:', exif.ExposureTime))
    </script>
    
    <!-- ESM Example -->
    <input id="filepicker" type="file" multiple>
    <script type="module">
      import exifr from './node_modules/exifr/dist/lite.esm.js'
      document.querySelector('#filepicker').addEventListener('change', async e => {
        let files = Array.from(e.target.files)
        let exifs = await Promise.all(files.map(exifr.parse))
        let dates = exifs.map(exif => exif.DateTimeOriginal.toGMTString())
        console.log(`${files.length} photos taken on:`, dates)
      })
    </script>
  6. Import exifr in Browsers

    master

    For browser environments, you can use ES Modules or classic UMD scripts. For older browsers like IE10, use the legacy build which includes polyfills (though you still need a Promise polyfill).

    <!-- ES Module in modern browsers -->
    <script type="module">import exifr from 'node_modules/exifr/dist/lite.esm.js';</script>
    
    <!-- classic UMD script -->
    <script src="https://cdn.jsdelivr.net/npm/exifr/dist/lite.umd.js"></script>
    
    <!-- IE10 & old browsers. You also need Promise polyfill -->
    <script src="https://cdn.jsdelivr.net/npm/exifr/dist/lite.legacy.umd.js"></script>
  7. Configure a custom bundle for web browsers

    master

    To minimize bundle size in web browsers, you can build a custom version of exifr by importing only the specific modules you need. The library is divided into four categories:

    1. (Chunked) File readers: BlobReader (browser), UrlFetcher (browser), FsReader (Node.js), Base64Reader.
    2. File parsers: Handles file formats like .jpg, .tiff, .heic.
    3. Segment parsers: Extracts data from formats like JFIF, TIFF, XMP, IPTC, ICC.
    4. Dictionaries: Controls the output format (keys and values).

    By importing from exifr/src/ instead of the main entry point, you can eliminate dead code and unused dictionaries.

    // Core bundle has nothing in it
    import * as exifr from 'exifr/src/core.mjs'
    // Now we import what we need
    import 'exifr/src/file-readers/BlobReader.mjs'
    import 'exifr/src/file-parsers/jpeg.mjs'
    import 'exifr/src/segment-parsers/icc.mjs'
    import 'exifr/src/dicts/icc-keys.mjs'
    import 'exifr/src/dicts/icc-values.mjs'
  8. Import exifr in Node.js

    master

    Exifr provides several ways to import the library depending on your Node.js environment and module system. For modern Node.js, you can use standard import syntax. For older environments, use require.

    // Modern Node.js can import CommonJS
    import exifr from 'exifr' // => exifr/dist/full.umd.cjs
    
    // Explicitly import ES Module
    import exifr from 'exifr/dist/full.esm.mjs' // to use ES Modules
    
    // CommonJS, old Node.js
    var exifr = require('exifr') // => exifr/dist/full.umd.cjs
  9. Configure tag filtering with `pick` and `skip`

    master

    To improve performance and reduce memory usage, you can tell exifr exactly which tags to read or which to ignore.

    • options.pick: An array of tags (strings or numeric codes) that should be parsed. All other blocks are disabled, and parsing stops once these are found.
    • options.skip: An array of tags that should be ignored. By default, MakerNote and UserComments are skipped.

    Tip: Using numeric tag codes is faster than string names as it avoids dictionary lookups.

    Examples:

    // Only extract specific EXIF tags
    {pick: ['ExposureTime', 'FNumber', 'ISO']}
    
    // Skip specific tags in a specific block
    {exif: {skip: ['ImageUniqueID', 42033, 'SubSecTimeDigitized']}}
    {
      pick: ['ExposureTime', 'FNumber', 'ISO']
    }
    
    {
      exif: {skip: ['ImageUniqueID', 42033, 'SubSecTimeDigitized']}
    }
  10. Configure Output Formatting

    master

    Control how the parsed metadata is structured and presented in the resulting object.

    • options.mergeOutput: (Default true) If true, all segments/blocks are merged into one flat object. If false, they are nested by segment/block name.
    • options.translateKeys: (Default true) Converts numeric tag codes to human-readable strings (e.g., 0x0110 $\rightarrow$ Model).
    • options.translateValues: (Default true) Converts raw enums to readable strings (e.g., 1 $\rightarrow$ Horizontal (normal)).
    • options.reviveValues: (Default true) Converts date strings into JavaScript Date instances.
    • options.sanitize: (Default true) Removes internal IFD pointers and unnecessary tags.
    • options.silentErrors: (Default true) Instead of throwing, errors are collected in output.errors.
  11. Optimize performance with the Chunked Reader

    master

    The chunked reader allows exifr to read only the necessary parts of a file instead of the whole thing. This is significantly faster and saves memory, especially for large files or network fetches.

    Key Options:

    • options.chunked: (Default true) Enables/disables chunked reading.
    • options.firstChunkSize: Size of the initial probe (Default: 512B in Node, 64KB in Browser).
    • options.chunkSize: Size of subsequent chunks (Default: 64KB).
    • options.chunkLimit: Max number of subsequent chunks to read (Default: 5). This prevents reading the whole file if metadata isn't found.
    • options.httpHeaders: Custom headers for URL fetches (e.g., for Authorization).

    Note: If using URLs, your server must support HTTP Range Requests. If range requests fail, set chunked: false.