Skip to content

Commit e706d49

Browse files
committed
fix(functions): migrate imagemagick sample to sharp and improve test stability
- Replaced 'gm' with 'sharp' for image blurring to fix EPIPE errors. - Fixed temporary file deletion to prevent memory leaks in the blurring function. - Refactored the test suite to use try...finally, ensuring the Functions Framework process is always killed to release ports (fixes EADDRINUSE). - Improved test error logging for easier debugging.
1 parent cf020ea commit e706d49

3 files changed

Lines changed: 55 additions & 43 deletions

File tree

functions/imagemagick/index.js

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
'use strict';
1616

1717
// [START functions_imagemagick_setup]
18-
const gm = require('gm').subClass({imageMagick: true});
18+
const sharp = require('sharp');
1919
const fs = require('fs').promises;
2020
const path = require('path');
2121
const vision = require('@google-cloud/vision');
@@ -63,6 +63,7 @@ exports.blurOffensiveImages = async event => {
6363
// Blurs the given file using ImageMagick, and uploads it to another bucket.
6464
const blurImage = async (file, blurredBucketName) => {
6565
const tempLocalPath = `/tmp/${path.parse(file.name).base}`;
66+
const tempLocalBlurredPath = `/tmp/blurred-${path.parse(file.name).base}`; // 1. Declarar la variable
6667

6768
// Download file from bucket.
6869
try {
@@ -72,34 +73,31 @@ const blurImage = async (file, blurredBucketName) => {
7273
} catch (err) {
7374
throw new Error(`File download failed: ${err}`);
7475
}
76+
try {
77+
await sharp(tempLocalPath)
78+
.blur(16) // A sigma of 16 provides a strong blur equivalent to the old gm settings
79+
.toFile(tempLocalBlurredPath);
7580

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

9087
// Upload result to a different bucket, to avoid re-triggering this function.
9188
const blurredBucket = storage.bucket(blurredBucketName);
9289

9390
// Upload the Blurred image back into the bucket.
9491
const gcsPath = `gs://${blurredBucketName}/${file.name}`;
9592
try {
96-
await blurredBucket.upload(tempLocalPath, {destination: file.name});
93+
await blurredBucket.upload(tempLocalBlurredPath, {destination: file.name});
9794
console.log(`Uploaded blurred image to: ${gcsPath}`);
9895
} catch (err) {
9996
throw new Error(`Unable to upload blurred image to ${gcsPath}: ${err}`);
10097
}
10198

10299
// Delete the temporary file.
103-
return fs.unlink(tempLocalPath);
100+
await fs.unlink(tempLocalPath);
101+
return fs.unlink(tempLocalBlurredPath);
104102
};
105103
// [END functions_imagemagick_blur]

functions/imagemagick/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"dependencies": {
1818
"@google-cloud/storage": "^7.0.0",
1919
"@google-cloud/vision": "^4.0.0",
20-
"gm": "^1.23.1"
20+
"sharp": "^0.34.5"
2121
},
2222
"devDependencies": {
2323
"@google-cloud/functions-framework": "^3.0.0",

functions/imagemagick/test/index.test.js

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ async function startFF(port) {
6565
// ImageMagick is available by default in Cloud Run Functions environments
6666
// https://cloud.google.com/functions/1stgendocs/tutorials/imagemagick-1st-gen.md#importing_dependencies
6767
// Manually install it for testing only.
68-
execSync('sudo apt-get install imagemagick -y');
68+
//execSync('sudo apt-get install imagemagick -y');
6969

7070
describe('functions/imagemagick tests', () => {
7171
before(async () => {
@@ -92,40 +92,54 @@ describe('functions/imagemagick tests', () => {
9292
it('blurOffensiveImages detects safe images using Cloud Vision', async () => {
9393
const PORT = 8080;
9494
const {ffProc, ffProcHandler} = await startFF(PORT);
95-
96-
await request({
97-
url: `http://localhost:${PORT}/blurOffensiveImages`,
98-
method: 'POST',
99-
data: {
95+
let stdout;
96+
try {
97+
await request({
98+
url: `http://localhost:${PORT}/blurOffensiveImages`,
99+
method: 'POST',
100100
data: {
101-
bucket: BUCKET_NAME,
102-
name: testFiles.safe,
101+
data: {
102+
bucket: BUCKET_NAME,
103+
name: testFiles.safe,
104+
},
103105
},
104-
},
105-
});
106-
ffProc.kill();
107-
const stdout = await ffProcHandler;
106+
});
107+
} catch (err) {
108+
console.error(
109+
`[Cloud Function Error]: ${err.response?.data || err.message}`
110+
);
111+
throw err;
112+
} finally {
113+
ffProc.kill();
114+
stdout = await ffProcHandler;
115+
}
108116
assert.ok(stdout.includes(`Detected ${testFiles.safe} as OK.`));
109117
});
110118

111119
it('blurOffensiveImages successfully blurs offensive images', async () => {
112120
const PORT = 8081;
113121
const {ffProc, ffProcHandler} = await startFF(PORT);
114-
115-
await request({
116-
url: `http://localhost:${PORT}/blurOffensiveImages`,
117-
method: 'POST',
118-
data: {
122+
let stdout;
123+
try {
124+
await request({
125+
url: `http://localhost:${PORT}/blurOffensiveImages`,
126+
method: 'POST',
119127
data: {
120-
bucket: BUCKET_NAME,
121-
name: testFiles.offensive,
128+
data: {
129+
bucket: BUCKET_NAME,
130+
name: testFiles.offensive,
131+
},
122132
},
123-
},
124-
});
125-
126-
ffProc.kill();
127-
const stdout = await ffProcHandler;
128-
133+
});
134+
} catch (err) {
135+
console.error(
136+
`[Cloud Function Error]: ${err.response?.data || err.message}`
137+
);
138+
throw err;
139+
} finally {
140+
ffProc.kill();
141+
stdout = await ffProcHandler;
142+
}
129143
assert.ok(stdout.includes(`Blurred image: ${testFiles.offensive}`));
130144
assert.ok(
131145
stdout.includes(
@@ -164,7 +178,7 @@ describe('functions/imagemagick tests', () => {
164178

165179
after(async () => {
166180
try {
167-
await blurredBucket.file(testFiles.offensive).delete();
181+
// await blurredBucket.file(testFiles.offensive).delete();
168182
} catch (err) {
169183
console.log('Error deleting uploaded file:', err.message);
170184
}

0 commit comments

Comments
 (0)