Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infinit loop with the moderate-images example #87

Closed
amsdamsgram opened this issue Mar 25, 2017 · 3 comments
Closed

Infinit loop with the moderate-images example #87

amsdamsgram opened this issue Mar 25, 2017 · 3 comments

Comments

@amsdamsgram
Copy link

amsdamsgram commented Mar 25, 2017

Hi,

I've copy/paste the moderate-images example and when I'm uploading an image in Firebase Storage, it executes the cloud function like there was a loop. To stop it, I have to comment the code and deploy again.

Here is the copy/pasted code. The only difference is that I have removed to google-cloud-vision part and blurred the image every time.

// functions/index.js

'use strict';

const functions = require('firebase-functions');
const mkdirp = require('mkdirp-promise');
const gcs = require('@google-cloud/storage')();
const exec = require('child-process-promise').exec;
const LOCAL_TMP_FOLDER = '/tmp/';

/**
 * When an image is uploaded we check if it is flagged as Adult or Violence by the Cloud Vision
 * API and if it is we blur it using ImageMagick.
 */
exports.blurOffensiveImages = functions.storage.object().onChange(event => {
  const object = event.data;
  const file = gcs.bucket(object.bucket).file(object.name);

  // Exit if this is a move or deletion event.
  if (object.resourceState === 'not_exists') {
    return console.log('This is a deletion event.');
  }

  // Check the image content using the Cloud Vision API.
    return blurImage(object.name, object.bucket, object.metadata);

});

/**
 * Blurs the given image located in the given bucket using ImageMagick.
 */
function blurImage(filePath, bucketName, metadata) {
  const filePathSplit = filePath.split('/');
  filePathSplit.pop();
  const fileDir = filePathSplit.join('/');
  const tempLocalDir = `${LOCAL_TMP_FOLDER}${fileDir}`;
  const tempLocalFile = `${LOCAL_TMP_FOLDER}${filePath}`;
  const bucket = gcs.bucket(bucketName);

  // Create the temp directory where the storage file will be downloaded.
  return mkdirp(tempLocalDir).then(() => {
    console.log('Temporary directory has been created', tempLocalDir);
    // Download file from bucket.
    return bucket.file(filePath).download({
      destination: tempLocalFile
    });
  }).then(() => {
    console.log('The file has been downloaded to', tempLocalFile);
    // Blur the image using ImageMagick.
    return exec(`convert ${tempLocalFile} -channel RGBA -blur 0x8 ${tempLocalFile}`);
  }).then(() => {
    console.log('Blurred image created at', tempLocalFile);
    // Uploading the Blurred image.
    return bucket.upload(tempLocalFile, {
      destination: filePath,
      metadata: {metadata: metadata} // Keeping custom metadata.
    });
  }).then(() => {
    console.log('Blurred image uploaded to Storage at', filePath);
  });
}

And here is a screenshot of my dashboard when I only uploaded one image called "oss2.png"
capture d ecran 2017-03-25 a 16 07 11

If you have any input on it, that would be great!

Thanks!

@amsdamsgram
Copy link
Author

amsdamsgram commented Mar 29, 2017

Hi,

After digging a little bit, it seems that functions.storage.object().onChange(event is causing the infinite loop.
Since it is listening to all changes of the storage and our function is uploading a new image at the end. It probably triggers an onchange event and here we go again?

EDIT: I did a small trick, it's not clean but it stops the infinite loop waiting for a better fix.
I've added optimized: true in the metadata:

    // Uploading the Blurred image.
    metadata.blurred: true;
    return bucket.upload(tempLocalFile, {
      destination: filePath,
      metadata: {metadata: metadata} // Keeping custom metadata.
    });

And then I checked on the onChange(event) if the image is already blurred or not:

if (object.metadata.blurred) {
    return console.log("Already blurred");
  }

Works fine, I have a log "Already blurred" and then it stops.

@nicolasgarnier
Copy link
Contributor

nicolasgarnier commented Mar 29, 2017

The trick is nice indeed :) (good way to use metadatas)

But normally the function does not run in a loop. The issue is that in your code it seems you have removed the Cloud Vision API call (this is what stops the loop) and you are always blurring the image (you were just testing this I guess).

In the original sample we first check if the image is offensive and, if it is, we blur it. The function gets called again on the new blurred image but - because it's been blurred - the image is not detected as being offensive on the second check and this ends the loop.

By the way be careful when using metadata because the users can set them from the client (when you upload the image) and they could use that to bypass the image check on the function. You can avoid that by using some special security rules to forbid setting the metadata from the client though.

@amsdamsgram
Copy link
Author

Thanks for your tips! I was not aware of metadata risk :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants