From 2edd926a8a903f1a3391bddfb176c7a5974d0fe4 Mon Sep 17 00:00:00 2001 From: "Gabriel L. Maljkovich" Date: Thu, 11 Jun 2020 18:27:39 -0300 Subject: [PATCH 1/5] Use heic-convert npm package to process images - Spawn one child process per request and convert images sequentially --- index.js | 36 +++++++++++++++++++++--------------- package.json | 1 + 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/index.js b/index.js index 77aebcc..b4e0230 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,5 @@ -const se = require('shell-escape'); +const fs = require('fs'); +const cp = require('child_process'); module.exports = function(options) { options = options || {}; @@ -7,7 +8,7 @@ module.exports = function(options) { 'image/heif': 1, 'image/heic': 1, 'image/heif-sequence': 1, - 'image/heic-sequence': 1 + 'image/heic-sequence': 1 }; return function(req, res, next) { if (!req.files) { @@ -25,6 +26,7 @@ module.exports = function(options) { } }); }); + const worker = cp.fork(`${__dirname}/worker.js`); Promise.all(relevant.map(file => { const newName = file.name.replace(/\.[^\.]+$/, '.jpg'); let newPath = file.path.replace(/\.[^\.]+$/, '.jpg'); @@ -33,25 +35,29 @@ module.exports = function(options) { } const newType = 'image/jpeg'; return new Promise((resolve, reject) => { - require('child_process').exec(se([ options.tifig, file.path, newPath ]), { encoding: 'utf8' }, function(error, stdout, stderr) { - if (error) { - console.log(stdout); - console.error(stderr); - return reject(error); - } - file.name = newName; - // Avoid leaking many megabytes of disk space - require('fs').unlinkSync(file.path); - file.path = newPath; - file.type = newType; - return resolve(true); + const errorHandler = (error) => { + console.error(error); + reject(error); + }; + worker.once('message', (_message) => { + file.name = newName; + // Avoid leaking many megabytes of disk space + fs.unlinkSync(file.path); + file.path = newPath; + file.type = newType; + // Avoid listener collision + worker.removeListener('error', errorHandler); + resolve(true); }); + worker.once('error', errorHandler); + worker.send({ inputPath: file.path, outputPath: newPath }); }); })).then(o => { + // Gracefully kill the child process + worker.send({ exit: true }); return next(); }).catch(e => { res.status(500).send('error'); }); } }; - diff --git a/package.json b/package.json index 1a9b39e..39af1d5 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ }, "homepage": "https://github.com/boutell/heic-to-jpeg-middleware#readme", "dependencies": { + "heic-convert": "^1.2.2", "shell-escape": "^0.2.0" } } From f79f202949ff0c811e4e80f6695e5dffb8358fbc Mon Sep 17 00:00:00 2001 From: "Gabriel L. Maljkovich" Date: Thu, 11 Jun 2020 18:38:54 -0300 Subject: [PATCH 2/5] Add worker.js --- worker.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 worker.js diff --git a/worker.js b/worker.js new file mode 100644 index 0000000..c594cc3 --- /dev/null +++ b/worker.js @@ -0,0 +1,17 @@ +const { promisify } = require('util'); +const fs = require('fs'); +const convert = require('heic-convert'); + +process.on('message', async ({ inputPath, outputPath, exit }) => { + if (exit) { + process.exit(0); + } + const inputBuffer = await promisify(fs.readFile)(inputPath); + const outputBuffer = await convert({ + buffer: inputBuffer, // the HEIC file buffer + format: 'JPEG', // output format + quality: 1 // the jpeg compression quality, between 0 and 1 + }); + await promisify(fs.writeFile)(outputPath, outputBuffer); + process.send(inputPath); +}); From ccf60b177d284bc0c19dd2ea896805eeaaa30cdf Mon Sep 17 00:00:00 2001 From: "Gabriel L. Maljkovich" Date: Fri, 12 Jun 2020 18:27:43 -0300 Subject: [PATCH 3/5] Update documentation and suggest release version 1.1.0 --- README.md | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 4d8a92a..cd26f09 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ```javascript const multipart = require('connect-multiparty')(); -const heicToJpeg = require('heic-to-jpeg')(tifig: '/opt/tifig'); +const heicToJpeg = require('heic-to-jpeg')(); const app = require('express')(); app.post('/upload', multipart, heicToJpeg, function(req, res) { // Access req.files as you normally would here. @@ -20,16 +20,6 @@ provides a `req.files` object in which each sub-object has `path`, `name` and `type` properties. The sub-objects of `req.files` may also be arrays of such objects. -[Requires the "tifig" command line utility. Install -that first.](https://github.com/monostream/tifig) You may specify its -path via `options.tifig`. If not it is assumed to be in the `PATH` -as `tifig`. - -## Limitations - -The `tifig` utility politely refuses to work on HEIF files -that didn't come from iOS 11. This is not a bug in the middleware. - ## Alternatives The very latest versions of ImageMagick support HEIF too. @@ -37,15 +27,16 @@ The very latest versions of ImageMagick support HEIF too. ## Warnings To prevent a denial of service, middleware that does CPU- and RAM-intensive -stuff like this should always be added only to the specific routes that +stuff like this should always be added only to the specific routes that require it. It's also a good idea to use other middleware to check the user's permissions first, rather than later in the route code itself. ## Changelog +1.1.0: Use [heic-convert](https://github.com/catdad-experiments/heic-convert) in replacement of [tifig](https://github.com/monostream/tifig). + 1.0.2: supports more types of file upload middleware. In particular, the sub-objects of `req.files` may be arrays, and if `path` does not have any extension to change then a `.jpg` extension is added. 1.0.1: more docs, repo push. 1.0.0: initial release. - From 3ee7945db89ce6dce92df8e526ad106f0e275868 Mon Sep 17 00:00:00 2001 From: "Gabriel L. Maljkovich" Date: Fri, 12 Jun 2020 18:34:23 -0300 Subject: [PATCH 4/5] Remove tifig from options --- index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/index.js b/index.js index b4e0230..93b87b9 100644 --- a/index.js +++ b/index.js @@ -3,7 +3,6 @@ const cp = require('child_process'); module.exports = function(options) { options = options || {}; - options.tifig = options.tifig || 'tifig'; const types = { 'image/heif': 1, 'image/heic': 1, From 5a3caa51277535df60ca045b46517caf4febce8a Mon Sep 17 00:00:00 2001 From: "Gabriel L. Maljkovich" Date: Thu, 18 Jun 2020 14:52:51 -0300 Subject: [PATCH 5/5] Update changelog with upcoming major release --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cd26f09..53e2eff 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ permissions first, rather than later in the route code itself. ## Changelog -1.1.0: Use [heic-convert](https://github.com/catdad-experiments/heic-convert) in replacement of [tifig](https://github.com/monostream/tifig). +2.0.0: Use [heic-convert](https://github.com/catdad-experiments/heic-convert) in replacement of [tifig](https://github.com/monostream/tifig). 1.0.2: supports more types of file upload middleware. In particular, the sub-objects of `req.files` may be arrays, and if `path` does not have any extension to change then a `.jpg` extension is added.