Skip to content

Commit 6edfeda

Browse files
authored
refactor(run/image-processing): replace deprecated gm library with sharp (#4297)
* refactor(run/image-processing): replace deprecated gm library with sharp * refactor: use fs promises API for file operations
1 parent 6cf803c commit 6edfeda

3 files changed

Lines changed: 29 additions & 25 deletions

File tree

run/image-processing/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Cloud Run Image Processing Sample
22

3-
This sample service applies [Cloud Storage](https://cloud.google.com/storage/docs)-triggered image processing with [Cloud Vision API](https://cloud.google.com/vision/docs) analysis and ImageMagick transformation.
3+
This sample service applies [Cloud Storage](https://cloud.google.com/storage/docs)-triggered image processing with [Cloud Vision API](https://cloud.google.com/vision/docs) analysis and sharp transformation.
44

55
Use it with the [Image Processing with Cloud Run tutorial](http://cloud.google.com/run/docs/tutorials/image-processing).
66

@@ -9,7 +9,7 @@ For more details on how to work with this sample read the [Google Cloud Run Node
99
## Dependencies
1010

1111
* **express**: Web server framework
12-
* **[gm](https://github.com/aheckmann/gm#readme)**: ImageMagick integration library.
12+
* **[sharp](https://sharp.pixelplumbing.com/)**: High-performance Node.js image processing library.
1313
* **@google-cloud/storage**: Google Cloud Storage client library.
1414
* **@google-cloud/vision**: Cloud Vision API client library.
1515

run/image-processing/image.js

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@
1515
'use strict';
1616

1717
// [START cloudrun_imageproc_handler_setup]
18-
const gm = require('gm').subClass({imageMagick: true});
19-
const fs = require('fs');
20-
const {promisify} = require('util');
18+
const fs = require('fs').promises;
19+
const sharp = require('sharp');
2120
const path = require('path');
2221
const vision = require('@google-cloud/vision');
2322

@@ -34,6 +33,13 @@ exports.blurOffensiveImages = async event => {
3433
// This event represents the triggering Cloud Storage object.
3534
const object = event;
3635

36+
if (object.bucket === BLURRED_BUCKET_NAME) {
37+
console.log(
38+
'Event triggered by the blurred bucket; skip to avoid recursion'
39+
);
40+
return;
41+
}
42+
3743
const file = storage.bucket(object.bucket).file(object.name);
3844
const filePath = `gs://${object.bucket}/${object.name}`;
3945

@@ -61,9 +67,10 @@ exports.blurOffensiveImages = async event => {
6167
// [END cloudrun_imageproc_handler_analyze]
6268

6369
// [START cloudrun_imageproc_handler_blur]
64-
// Blurs the given file using ImageMagick, and uploads it to another bucket.
70+
// Blurs the given file using sharp, and uploads it to another bucket.
6571
const blurImage = async (file, blurredBucketName) => {
6672
const tempLocalPath = `/tmp/${path.parse(file.name).base}`;
73+
const tempLocalBlurredPath = `/tmp/blurred-${path.parse(file.name).base}`;
6774

6875
// Download file from bucket.
6976
try {
@@ -74,34 +81,31 @@ const blurImage = async (file, blurredBucketName) => {
7481
throw new Error(`File download failed: ${err}`);
7582
}
7683

77-
await new Promise((resolve, reject) => {
78-
gm(tempLocalPath)
79-
.blur(0, 16)
80-
.write(tempLocalPath, (err, stdout) => {
81-
if (err) {
82-
console.error('Failed to blur image.', err);
83-
reject(err);
84-
} else {
85-
console.log(`Blurred image: ${file.name}`);
86-
resolve(stdout);
87-
}
88-
});
89-
});
84+
try {
85+
await sharp(tempLocalPath).blur(16).toFile(tempLocalBlurredPath);
86+
87+
console.log(`Blurred image: ${file.name}`);
88+
} catch (err) {
89+
console.error('Failed to blur image.', err);
90+
throw err;
91+
}
9092

9193
// Upload result to a different bucket, to avoid re-triggering this function.
9294
const blurredBucket = storage.bucket(blurredBucketName);
9395

9496
// Upload the Blurred image back into the bucket.
9597
const gcsPath = `gs://${blurredBucketName}/${file.name}`;
9698
try {
97-
await blurredBucket.upload(tempLocalPath, {destination: file.name});
99+
await blurredBucket.upload(tempLocalBlurredPath, {destination: file.name});
98100
console.log(`Uploaded blurred image to: ${gcsPath}`);
99101
} catch (err) {
100102
throw new Error(`Unable to upload blurred image to ${gcsPath}: ${err}`);
103+
} finally {
104+
// Delete the temporary file.
105+
await Promise.allSettled([
106+
fs.unlink(tempLocalPath),
107+
fs.unlink(tempLocalBlurredPath),
108+
]);
101109
}
102-
103-
// Delete the temporary file.
104-
const unlink = promisify(fs.unlink);
105-
return unlink(tempLocalPath);
106110
};
107111
// [END cloudrun_imageproc_handler_blur]

run/image-processing/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"@google-cloud/storage": "^7.0.0",
2323
"@google-cloud/vision": "^4.0.0",
2424
"express": "^4.16.4",
25-
"gm": "^1.23.1"
25+
"sharp": "^0.34.5"
2626
},
2727
"devDependencies": {
2828
"c8": "^10.0.0",

0 commit comments

Comments
 (0)