-1

I'm having trouble using npm exiftool. (https://www.npmjs.com/package/exiftool) I'm trying to do some stuffs using that.

  1. Iterate images files through specific folder
  2. Get 'xpKeywords' data of each image files.
  3. Write file storing data.

Here's my code.

const fs = require('fs');
const exif = require('exiftool');
const folderName = 'testImages';
const inputPath = `/Users/myName/Project/project/${folderName}`;
const files = fs.readdirSync(inputPath, 'utf8');

let data = [];

(async () => {
  let promises = [];
  files.forEach(file => promises.push(fs.readFileSync(`${inputPath}/${file}`)));
  let results = await Promise.all(promises);
  for(let [index, result] of results.entries()) {
    let datum = await getMetadata(result, index);
    console.log("out");
    data.push(datum);
  }
  fs.writeFileSync('outputData/metaData.json', JSON.stringify(data, null, 4), (error) => {
    console.log('Error Occurred');
  });
})();

async function getMetadata(result, index) {
  console.log(`get metadata ${index}`);
  await exif.metadata(result, (err, metadata) => {
    return {
      name: files[index],
      hashTags: metadata.xpKeywords
    };
  });
}

After running that code, the file metaData.json is not what I'd expected.

[ null, null, null, null, null, null ]

I think I'm having some trouble about the usage of async function in function getMetadata.

Hyeon
  • 45
  • 5

1 Answers1

0

There are several problems there.

  • The chief one is that getMetaData doesn't return anything. Returning from the callback that you give exif.metadata doesn't do anything to set the return value of getMetaData.
  • Separately, fs.readFileSync doesn't return a promise, so there's no point to the let results = await Promise.all(promises); call. (As the name suggests, it does its work synchronously.)
  • Referencing files via indexes into a module global is error-prone.

To use promises, you can use Node.js's currently-experimental promises file API, or use util.promisify on fs.readFile (that's the version that's actually asynchronous, using a callback). You can also use promisify on exif.metadata if you want to get a promise-enabled version of that.

This question's answers go into more detail about converting a callback-based API to promises.

Keeping all that in mind, I think you're looking for something along these lines (may need tweaking):

const fs = require('fs');
const {promisify} = require('util');
const exif = require('exiftool');

const pReaddir = promisify(fs.readdir);
const pReadFile = promisify(fs.readFile);
const pWriteFile = promisify(fs.writeFile);
const pMetadata = promisify(exif.metadata);

const folderName = 'testImages';
const inputPath = `/Users/myName/Project/project/${folderName}`;

(async () => {
    // Get the file names
    const files = await pReaddir(inputPath, 'utf8');
    // Process the files in parallel and wait for the result
    const data = Promise.all(files.map(async(name) => {
        // Read this file and get its metadata from it
        let {xpKeywords: hashTags} = await pMetadata(await pReadFile(`${inputPath}/${name}`));
        // Return an object with the file's name and xpKeywords as its hashTags
        return {name, hashTags};
    }));
    // Write the results to file
    await pWriteFile('outputData/metaData.json', JSON.stringify(data, null, 4));
})()
.catch(error => {
    console.error(error);
});
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875