Gatsby: [gatsby-plugin-sharp] Access to image EXIF/metadata?

Created on 9 Apr 2018  路  8Comments  路  Source: gatsbyjs/gatsby

This is just a general question - apologies if it's not the appropriate place to ask.

I'm looking at using Gatsby to build image galleries and would quite like to hook into the EXIF data for things like captions/copyright/potentially geodata/etc.

A quick skim of the Sharp docs suggests you can get access to that stuff and I was wondering if the gatsby plugin could be modified to retrieve that, or if I'm barking up the wrong tree...?

help wanted

Most helpful comment

Hello! Here's what I ended up doing...

I'm using exiftool to set fields for some of the non-standard fields like this

exiftool -DocumentName='Savannah, Ga' -ImageDescription='Bridge At Night' -ImageHistory='Places' 20180219-_MG_9929_HDR.jpg

Then extracting exif data using fast-exif on build:

// package.json

{
"dependencies": {
  "fast-exif": "^1.0.1",
  "lodash": "^4.17.5",
  "gatsby-plugin-sharp": "^1.6.27",
 }
}

```js
// gatsby-config.js

modules.exports = {
plugins: [
{
resolve: gatsby-source-filesystem,
options: {
name: pictures,
path: ${__dirname}/content/pictures/,
plugins: [
gatsby-plugin-sharp
]
}
},
gatsby-plugin-sharp,
gatsby-transformer-sharp,
]
}


```js
// gatsby-node.js

const fastExif = require('fast-exif');
const get = require('lodash/get');

exports.onCreateNode = ({ node, getNode, boundActionCreators }) => {
  const { createNodeField } = boundActionCreators;

if(node.internal.type === 'ImageSharp' && node.id.includes('content/pictures/')) {
      const absolutePath = node.id.split(' ')[0];
      fastExif.read(absolutePath)
        .then((exifData) => {
          const title        = get( exifData, [ 'image', 'ImageDescription' ], null );
          const location     = get( exifData, [ 'image', 'DocumentName' ], null );
          const categoryData = get( exifData, [ 'exif', 'ImageHistory' ], null );
          const categories   = categoryData === null ? [ 'uncategorized' ] : categoryData.split( ',' );
          const iso          = get( exifData, [ 'exif', 'ISO' ], null );
          const model        = get( exifData, [ 'exif', 'LensModel' ], null );
          const fstop        = get( exifData, [ 'exif', 'FNumber' ], null );
          const focalLength  = get( exifData, [ 'exif', 'FocalLength' ], null );

              createNodeField({
                node,
                name: 'exif',
                value: {title, location, categories, technical: {iso, model, fstop, focalLength}}
              });
        })
        .catch((err) => console.error(err));
  }
}

On my photos page I pull the data using graphql and display using react-masonry-component for layout and react-image-lightbox for individual photos displaying which has handy props for displaying a caption box where I put the exif data.

export const guery = graphql`
  query PhotosQuery {
    photos: allImageSharp(filter: { id: { regex: "//pictures//" } }) {
       edges {
          node {
            original {
              width
              height
              src
            },
            sizes(maxWidth: 1600) {
              src,
              srcSet,
              sizes
            },
            resolutions(width: 200) {
            src,
            srcSet,
            width,
            height,
            aspectRatio,
            base64
            }
            fields {
              exif {
                title
                location
                categories
                technical {
                  iso
                  model
                  fstop
                  focalLength
                }
              }
            }
          }
      }
    }
  }
`;

You can see the result here for some of the photos -- the BMW pic is a good example (screenshot)

All 8 comments

It's ok to ask here :)

This would be great candidate to create separate plugin - maybe gatsby-transformer-exif? It then could use sharp, node-exif or whatever other dependency (if this would be in separate package we are not tied to sharp then).

I can guide you if you want to create this plugin.

Someone on the Discord server did something similar. Maybe you can connect with him there.

image

Thanks, folks! I'll definitely look in to contributing to a plugin!

@foxxmd

Hello! Here's what I ended up doing...

I'm using exiftool to set fields for some of the non-standard fields like this

exiftool -DocumentName='Savannah, Ga' -ImageDescription='Bridge At Night' -ImageHistory='Places' 20180219-_MG_9929_HDR.jpg

Then extracting exif data using fast-exif on build:

// package.json

{
"dependencies": {
  "fast-exif": "^1.0.1",
  "lodash": "^4.17.5",
  "gatsby-plugin-sharp": "^1.6.27",
 }
}

```js
// gatsby-config.js

modules.exports = {
plugins: [
{
resolve: gatsby-source-filesystem,
options: {
name: pictures,
path: ${__dirname}/content/pictures/,
plugins: [
gatsby-plugin-sharp
]
}
},
gatsby-plugin-sharp,
gatsby-transformer-sharp,
]
}


```js
// gatsby-node.js

const fastExif = require('fast-exif');
const get = require('lodash/get');

exports.onCreateNode = ({ node, getNode, boundActionCreators }) => {
  const { createNodeField } = boundActionCreators;

if(node.internal.type === 'ImageSharp' && node.id.includes('content/pictures/')) {
      const absolutePath = node.id.split(' ')[0];
      fastExif.read(absolutePath)
        .then((exifData) => {
          const title        = get( exifData, [ 'image', 'ImageDescription' ], null );
          const location     = get( exifData, [ 'image', 'DocumentName' ], null );
          const categoryData = get( exifData, [ 'exif', 'ImageHistory' ], null );
          const categories   = categoryData === null ? [ 'uncategorized' ] : categoryData.split( ',' );
          const iso          = get( exifData, [ 'exif', 'ISO' ], null );
          const model        = get( exifData, [ 'exif', 'LensModel' ], null );
          const fstop        = get( exifData, [ 'exif', 'FNumber' ], null );
          const focalLength  = get( exifData, [ 'exif', 'FocalLength' ], null );

              createNodeField({
                node,
                name: 'exif',
                value: {title, location, categories, technical: {iso, model, fstop, focalLength}}
              });
        })
        .catch((err) => console.error(err));
  }
}

On my photos page I pull the data using graphql and display using react-masonry-component for layout and react-image-lightbox for individual photos displaying which has handy props for displaying a caption box where I put the exif data.

export const guery = graphql`
  query PhotosQuery {
    photos: allImageSharp(filter: { id: { regex: "//pictures//" } }) {
       edges {
          node {
            original {
              width
              height
              src
            },
            sizes(maxWidth: 1600) {
              src,
              srcSet,
              sizes
            },
            resolutions(width: 200) {
            src,
            srcSet,
            width,
            height,
            aspectRatio,
            base64
            }
            fields {
              exif {
                title
                location
                categories
                technical {
                  iso
                  model
                  fstop
                  focalLength
                }
              }
            }
          }
      }
    }
  }
`;

You can see the result here for some of the photos -- the BMW pic is a good example (screenshot)

Wow that's mega, thanks @FoxxMD! I'll dive into that a bit deeper soon.

Would love to see a plugin that wraps fast-exif!

As a reference to everybody who's still seeing this, I published a tiny plugin that wraps fast-exif for gatsby-plugin-sharp here (GitHub).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

3CordGuy picture 3CordGuy  路  3Comments

Oppenheimer1 picture Oppenheimer1  路  3Comments

mikestopcontinues picture mikestopcontinues  路  3Comments

magicly picture magicly  路  3Comments

andykais picture andykais  路  3Comments