3

Exiftool can not be spawned in aws lambda environment, getting below error:

ERROR   { Error: spawn /var/task/node_modules/dist-exiftool/node_modules/exiftool.pl/vendor/exiftool ENOENT
at Process.ChildProcess._handle.onexit (internal/child_process.js:240:19)
at onErrorNT (internal/child_process.js:415:16)
at process._tickCallback (internal/process/next_tick.js:63:19)
errno: 'ENOENT',
code: 'ENOENT',
  syscall:
   'spawn /var/task/node_modules/dist-exiftool/node_modules/exiftool.pl/vendor/exiftool',
  path:
   '/var/task/node_modules/dist-exiftool/node_modules/exiftool.pl/vendor/exiftool',
  spawnargs:
   [ '-echo2', '1574158488325', '-stay_open', 'True', '-@', '-' ] }

A function is working properly with NodeJs version 8.10 on AWS Lamda but I want to upgrade this function to NodeJs 10.X version. I failed to run a function on NodeJs 10.X. I am always getting an error on followings line.

const ep = new exiftool.ExiftoolProcess(exiftoolBin);

My function:

const exiftool =  require('node-exiftool')
const exiftoolBin = require('dist-exiftool')

const fs = require('fs')
const path = require('path')

const ep = new exiftool.ExiftoolProcess(exiftoolBin)

const PHOTO_PATH = path.join(__dirname, 'photo.jpg')
const rs = fs.createReadStream(PHOTO_PATH)

ep.open()
    .then(() => ep.readMetadata(rs, ['-File:all']))
    .then((res) => {
        console.log(res)
    })
    .then(() => ep.close(), () => ep.close())
    .catch(console.error) 

Please help me to resolve this issue.

Thank you.

1 Answers1

4

The error message is rather misleading but there are some hints. The path says /exiftool.pl/, pl usually being perl. If you open the exiftool file you'll see #!/usr/bin/perl -w on the first line.

What's actually missing (ENOENT) is a perl binary

AWS apparently removed various binaries from the runtime environments. It seems the best practice now is using Layers to add missing dependencies.

So first, add perl to your AWS Lambda Function as a Layer, to do that you could use an ARN from https://metacpan.org/pod/AWS::Lambda. The perl binary should then be available at /opt/bin/perl, which doesn't match the #! of the script, so we have to adjust the function code:

const { ExiftoolProcess } = require('node-exiftool');
const exiftoolBin = require('dist-exiftool');

// Get perl binary path from environment variables or fallback to default.
const perlBin = process.env.PERL_BIN || '/opt/bin/perl';
// Instead of calling the perl script directly causing #! to choose the
// interpreter path, we'll pass the script path to the interpreter directly. 
const exiftool = new ExiftoolProcess(`${perlBin} ${exiftoolBin}`);

But now the path passed to ExiftoolProcess is no longer a valid filepath. It's a shell command that is passed on to child_process.spawn(). Therefore we have to make sure it's handled as such by doing:

exiftool.open({ shell: true })

You might be done at this point, or you might run into the following error: perl: error while loading shared libraries: libcrypt.so.1: cannot open shared object file: No such file or directory. I'm not sure how to best resolve this problem. Maybe compiling perl yourself and creating a layer on your own prevents it. But the quick and dirty solution that worked for me was copying the libcrypt.so.1 file from my local ubuntu system ($ whereis libcrypt.so.1), putting it into a folder called lib, zipping that folder and uploading it as a new layer.

patrickd
  • 186
  • 1
  • 12