מדריך לעיבוד תמונות מ-Cloud Storage

במדריך הזה נדגים איך להשתמש ב-Cloud Run, ב-Cloud Vision API וב-ImageMagick כדי לזהות תמונות פוגעניות שהועלו לקטגוריה של Cloud Storage ולטשטש אותן. המדריך הזה מבוסס על המדריך שימוש ב-Pub/Sub עם Cloud Run.

במדריך הזה מוסבר איך לשנות אפליקציה לדוגמה קיימת. אפשר גם להוריד את האפליקציה לדוגמה המלאה.

מטרות

  • כתיבה, פיתוח ופריסה של שירות אסינכרוני לעיבוד נתונים ב-Cloud Run.
  • מפעילים את השירות על ידי העלאת קובץ ל-Cloud Storage ויצירת הודעת Pub/Sub.
  • אפשר להשתמש ב-Cloud Vision API כדי לזהות תוכן אלים או תוכן למבוגרים בלבד.
  • אפשר להשתמש ב-ImageMagick כדי לטשטש תמונות פוגעניות.
  • כדי לבדוק את השירות, מעלים תמונה של זומבי שאוכל בשר.

עלויות

במסמך הזה משתמשים ברכיבים הבאים של Google Cloud, והשימוש בהם כרוך בתשלום:

כדי להעריך את ההוצאות בהתאם לתחזית השימוש שלכם, אתם יכולים להיעזר במחשבון העלויות.

משתמשים חדשים של Google Cloud ? יכול להיות שאתם זכאים לתקופת ניסיון בחינם.

לפני שמתחילים

  1. נכנסים לחשבון Google Cloud . אם אתם משתמשים חדשים ב- Google Cloud, צרו חשבון כדי שתוכלו להעריך את הביצועים של המוצרים שלנו בתרחישים מהעולם האמיתי. לקוחות חדשים מקבלים בחינם גם קרדיט בשווי 300$ להרצה, לבדיקה ולפריסה של עומסי העבודה.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  5. Verify that billing is enabled for your Google Cloud project.

  6. מפעילים את ממשקי ה-API של Artifact Registry, ‏ Cloud Build, ‏ Pub/Sub,‏ Cloud Run, ‏ Cloud Storage ו-Cloud Vision.

    תפקידים שנדרשים להפעלת ממשקי API

    כדי להפעיל ממשקי API, צריך את תפקיד ה-IAM 'אדמין של Service Usage' (roles/serviceusage.serviceUsageAdmin), שכולל את ההרשאה serviceusage.services.enable. איך מקצים תפקידים

    הפעלת ממשקי ה-API

  7. מתקינים ומפעילים את ה-CLI של gcloud.
  8. עדכון רכיבים:
    gcloud components update
  9. מגדירים נושא Pub/Sub, מינוי מאובטח לשליחת הודעות ופונקציית Cloud Run ראשונית לטיפול בהודעות, לפי המדריך לשימוש ב-Pub/Sub.

התפקידים הנדרשים

כדי לקבל את ההרשאות שדרושות להשלמת המדריך, צריך לבקש מהאדמין להקצות לכם את תפקידי ה-IAM הבאים בפרויקט:

להסבר על מתן תפקידים, ראו איך מנהלים את הגישה ברמת הפרויקט, התיקייה והארגון.

יכול להיות שאפשר לקבל את ההרשאות הנדרשות גם באמצעות תפקידים בהתאמה אישית או תפקידים מוגדרים מראש.

הגדרת ברירות מחדל ב-gcloud

כדי להגדיר את gcloud עם ערכי ברירת מחדל לשירות Cloud Run:

  1. מגדירים את פרויקט ברירת המחדל:

    gcloud config set project PROJECT_ID

    מחליפים את PROJECT_ID בשם הפרויקט שיצרתם לצורך המדריך הזה.

  2. מגדירים את gcloud לאזור שבחרתם:

    gcloud config set run/region REGION

    מחליפים את REGION באזור נתמך ב-Cloud Run לבחירתכם.

מיקומים של Cloud Run

‫Cloud Run הוא שירות אזורי, כלומר התשתית שמריצה את שירותי Cloud Run ממוקמת באזור ספציפי ומנוהלת על ידי Google כך שתהיה זמינה באופן יתירתי בכל התחומים באותו אזור.

הקריטריונים העיקריים לבחירת האזור שבו יפעלו שירותי Cloud Run הם זמן האחזור, הזמינות או העמידות שנדרשים לכם. בדרך כלל אפשר לבחור את האזור הקרוב ביותר למשתמשים, אבל כדאי לקחת בחשבון את המיקום של מוצרים Google Cloud אחרים שבהם נעשה שימוש בשירות Cloud Run. השימוש במוצרים של Google Cloud Google Cloud יחד בכמה מיקומים יכול להשפיע על זמן האחזור ועל העלות של השירות.

‫Cloud Run זמין באזורים הבאים:

בכפוף לתמחור ברמה 1

בכפוף לתמחור ברמה 2

  • africa-south1 (יוהנסבורג)
  • asia-east2 (הונג קונג)
  • asia-northeast3 (סיאול, קוריאה הדרומית)
  • asia-southeast1 (סינגפור)
  • asia-southeast2 (ג'אקארטה)
  • asia-south2 (דלהי, הודו)
  • australia-southeast1 (סידני)
  • australia-southeast2 (מלבורן)
  • europe-central2 (ורשה, פולין)
  • europe-west10 (Berlin)
  • europe-west12 (טורינו)
  • europe-west2 (לונדון, בריטניה) סמל של עלה רמה נמוכה של CO2
  • europe-west3 (פרנקפורט, גרמניה)
  • europe-west6 (ציריך, שווייץ) סמל של עלה רמה נמוכה של CO2
  • me-central1 (דוחה)
  • me-central2 (דמאם)
  • northamerica-northeast1 (מונטריאול) סמל של עלה רמה נמוכה של CO2
  • northamerica-northeast2 (טורונטו) סמל של עלה רמה נמוכה של CO2
  • southamerica-east1 (סאו פאולו, ברזיל) סמל של עלה רמה נמוכה של CO2
  • southamerica-west1 (סנטיאגו, צ'ילה) סמל של עלה רמה נמוכה של CO2
  • us-west2 (לוס אנג'לס)
  • us-west3 (סולט לייק סיטי)
  • us-west4 (לאס וגאס)

אם כבר יצרתם שירות Cloud Run, תוכלו לראות את האזור בלוח הבקרה של Cloud Run בGoogle Cloud מסוף.

הסבר על רצף הפעולות

במדריך הזה, זרימת הנתונים מתבצעת לפי השלבים הבאים:

  1. משתמש מעלה תמונה לקטגוריה של Cloud Storage.
  2. ‫Cloud Storage מפרסם הודעה על הקובץ החדש ב-Pub/Sub.
  3. שירות Pub/Sub דוחף את ההודעה לשירות Cloud Run.
  4. השירות Cloud Run מאחזר את קובץ האימג' שמופיע בהודעה ב-Pub/Sub.
  5. שירות Cloud Run משתמש ב-Cloud Vision API כדי לנתח את התמונה.
  6. אם מזוהה תוכן אלים או תוכן למבוגרים בלבד, שירות Cloud Run משתמש ב-ImageMagick כדי לטשטש את התמונה.
  7. שירות Cloud Run מעלה את התמונה המטושטשת לקטגוריה אחרת של Cloud Storage לשימוש.

השימוש הבא בתמונה המטושטשת נשאר כתרגיל לקורא.

יצירת מאגר רגיל ב-Artifact Registry

יוצרים מאגר רגיל של Artifact Registry לאחסון קובץ האימג' של הקונטיינר:

gcloud artifacts repositories create REPOSITORY \
    --repository-format=docker \
    --location=REGION

מחליפים את:

  • REPOSITORY בשם ייחודי למאגר.
  • REGION עם האזור שבו ישמש מאגר Artifact Registry. Google Cloud

הגדרת קטגוריות של Cloud Storage

gcloud

  1. יוצרים קטגוריה של Cloud Storage להעלאת תמונות, כאשר INPUT_BUCKET_NAME הוא שם קטגוריה ייחודי גלובלית:

    gcloud storage buckets create gs://INPUT_BUCKET_NAME

    שירות Cloud Run קורא רק מהקטגוריה הזו.

  2. יוצרים קטגוריה שנייה ב-Cloud Storage לקבלת תמונות מטושטשות, כאשר BLURRED_BUCKET_NAME הוא שם קטגוריה ייחודי גלובלית:

    gcloud storage buckets create gs://BLURRED_BUCKET_NAME

    השירות Cloud Run מעלה את התמונות המטושטשות לדלי הזה. שימוש בדלי נפרד מונע מתמונות שעברו עיבוד להפעיל מחדש את השירות.

    כברירת מחדל, עדכונים ב-Cloud Run מופעלים בתור חשבון השירות שמוגדר כברירת מחדל ב-Compute Engine.

    אם אתם משתמשים בחשבון שירות שמנוהל על ידי המשתמש, אתם צריכים לוודא שהקציתם את תפקידי ה-IAM הנדרשים כדי שלחשבון השירות תהיה הרשאת storage.objects.get לקריאה מ-INPUT_BUCKET_NAME והרשאת storage.objects.create להעלאה אל BLURRED_BUCKET_NAME.

Terraform

כדי ללמוד איך להחיל הגדרות ב-Terraform או להסיר אותן, ראו פקודות בסיסיות ב-Terraform.

יוצרים שתי קטגוריות של Cloud Storage: אחת להעלאת תמונות מקוריות ואחת לשירות Cloud Run להעלאת תמונות מטושטשות.

כדי ליצור את שתי הקטגוריות של Cloud Storage עם שמות ייחודיים גלובלית, מוסיפים את הקוד הבא לקובץ main.tf הקיים:

resource "random_id" "bucket_suffix" {
  byte_length = 8
}

resource "google_storage_bucket" "imageproc_input" {
  name     = "input-bucket-${random_id.bucket_suffix.hex}"
  location = "us-central1"
}

output "input_bucket_name" {
  value = google_storage_bucket.imageproc_input.name
}

resource "google_storage_bucket" "imageproc_output" {
  name     = "output-bucket-${random_id.bucket_suffix.hex}"
  location = "us-central1"
}

output "blurred_bucket_name" {
  value = google_storage_bucket.imageproc_output.name
}

כברירת מחדל, עדכונים ב-Cloud Run מופעלים בתור חשבון השירות שמוגדר כברירת מחדל ב-Compute Engine.

אם אתם משתמשים בחשבון שירות שמנוהל על ידי המשתמש, אתם צריכים לוודא שהקציתם את תפקידי ה-IAM הנדרשים כדי שלחשבון תהיה הרשאת storage.objects.get לקריאה מ-google_storage_bucket.imageproc_input והרשאת storage.objects.create להעלאה אל google_storage_bucket.imageproc_output.

בשלבים הבאים תיצרו ותפעילו שירות לעיבוד הודעות על העלאות קבצים אל INPUT_BUCKET_NAME. כדי למנוע הפעלה מוקדמת של השירות החדש, מפעילים את שליחת ההתראות אחרי שמבצעים פריסה ובדיקה של השירות.

שינוי קוד לדוגמה במדריך בנושא Pub/Sub

המדריך הזה מבוסס על הקוד שנוצר במדריך לשימוש ב-Pub/Sub. אם עדיין לא השלמתם את ההדרכה הזו, עכשיו זה הזמן. דלגו על שלבי הניקוי וחזרו לכאן כדי להוסיף התנהגות של עיבוד תמונות.

הוספת קוד לעיבוד תמונות

קוד עיבוד התמונה מופרד מטיפול בבקשות כדי לשפר את הקריאות ולאפשר בדיקה קלה יותר. כדי להוסיף קוד לעיבוד תמונה:

  1. עוברים לספרייה של הקוד לדוגמה של המדריך בנושא Pub/Sub.

  2. מוסיפים קוד לייבוא יחסי התלות של עיבוד התמונה, כולל ספריות לשילוב עם Google Cloud שירותים, ImageMagick ומערכת הקבצים.

    Node.js

    פותחים קובץ image.js חדש בכלי העריכה ומעתיקים אליו את הטקסט הבא:
    const fs = require('fs').promises;
    const sharp = require('sharp');
    const path = require('path');
    const vision = require('@google-cloud/vision');
    
    const {Storage} = require('@google-cloud/storage');
    const storage = new Storage();
    const client = new vision.ImageAnnotatorClient();
    
    const {BLURRED_BUCKET_NAME} = process.env;

    Python

    פותחים קובץ image.py חדש בכלי העריכה ומעתיקים אליו את הטקסט הבא:
    import os
    import tempfile
    
    from google.cloud import storage, vision
    from wand.image import Image
    
    storage_client = storage.Client()
    vision_client = vision.ImageAnnotatorClient()

    המשך

    פותחים קובץ imagemagick/imagemagick.go חדש בכלי העריכה ומעתיקים אליו את הטקסט הבא:
    
    // Package imagemagick contains an example of using ImageMagick to process a
    // file uploaded to Cloud Storage.
    package imagemagick
    
    import (
    	"context"
    	"errors"
    	"fmt"
    	"log"
    	"os"
    	"os/exec"
    
    	"cloud.google.com/go/storage"
    	vision "cloud.google.com/go/vision/apiv1"
    	"cloud.google.com/go/vision/v2/apiv1/visionpb"
    )
    
    // Global API clients used across function invocations.
    var (
    	storageClient *storage.Client
    	visionClient  *vision.ImageAnnotatorClient
    )
    
    func init() {
    	// Declare a separate err variable to avoid shadowing the client variables.
    	var err error
    
    	storageClient, err = storage.NewClient(context.Background())
    	if err != nil {
    		log.Fatalf("storage.NewClient: %v", err)
    	}
    
    	visionClient, err = vision.NewImageAnnotatorClient(context.Background())
    	if err != nil {
    		log.Fatalf("vision.NewAnnotatorClient: %v", err)
    	}
    }
    

    Java

    פותחים קובץ src/main/java/com/example/cloudrun/ImageMagick.java חדש בעורך ומעתיקים אליו את הטקסט הבא:
    import com.google.cloud.storage.Blob;
    import com.google.cloud.storage.BlobId;
    import com.google.cloud.storage.BlobInfo;
    import com.google.cloud.storage.Storage;
    import com.google.cloud.storage.StorageOptions;
    import com.google.cloud.vision.v1.AnnotateImageRequest;
    import com.google.cloud.vision.v1.AnnotateImageResponse;
    import com.google.cloud.vision.v1.BatchAnnotateImagesResponse;
    import com.google.cloud.vision.v1.Feature;
    import com.google.cloud.vision.v1.Feature.Type;
    import com.google.cloud.vision.v1.Image;
    import com.google.cloud.vision.v1.ImageAnnotatorClient;
    import com.google.cloud.vision.v1.ImageSource;
    import com.google.cloud.vision.v1.SafeSearchAnnotation;
    import com.google.gson.JsonObject;
    import java.io.IOException;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.ArrayList;
    import java.util.List;
    
    public class ImageMagick {
    
      private static final String BLURRED_BUCKET_NAME = System.getenv("BLURRED_BUCKET_NAME");
      private static Storage storage = StorageOptions.getDefaultInstance().getService();

  3. מוסיפים קוד לקבלת הודעת Pub/Sub כאובייקט אירוע ולשליטה בעיבוד התמונה.

    האירוע מכיל נתונים על התמונה שהועלתה במקור. הקוד הזה קובע אם צריך לטשטש את התמונה. הוא עושה זאת על ידי בדיקת התוצאות של ניתוח Cloud Vision כדי לזהות תוכן אלים או תוכן למבוגרים בלבד.

    Node.js

    // Blurs uploaded images that are flagged as Adult or Violence.
    exports.blurOffensiveImages = async event => {
      // This event represents the triggering Cloud Storage object.
      const object = event;
    
      if (object.bucket === BLURRED_BUCKET_NAME) {
        console.log(
          'Event triggered by the blurred bucket; skip to avoid recursion'
        );
        return;
      }
    
      const file = storage.bucket(object.bucket).file(object.name);
      const filePath = `gs://${object.bucket}/${object.name}`;
    
      console.log(`Analyzing ${file.name}.`);
    
      try {
        const [result] = await client.safeSearchDetection(filePath);
        const detections = result.safeSearchAnnotation || {};
    
        if (
          // Levels are defined in https://cloud.google.com/vision/docs/reference/rest/v1/AnnotateImageResponse#likelihood
          detections.adult === 'VERY_LIKELY' ||
          detections.violence === 'VERY_LIKELY'
        ) {
          console.log(`Detected ${file.name} as inappropriate.`);
          return blurImage(file, BLURRED_BUCKET_NAME);
        } else {
          console.log(`Detected ${file.name} as OK.`);
        }
      } catch (err) {
        console.error(`Failed to analyze ${file.name}.`, err);
        throw err;
      }
    };

    Python

    def blur_offensive_images(data):
        """Blurs uploaded images that are flagged as Adult or Violence.
    
        Args:
            data: Pub/Sub message data
        """
        file_data = data
    
        file_name = file_data["name"]
        bucket_name = file_data["bucket"]
    
        blob = storage_client.bucket(bucket_name).get_blob(file_name)
        blob_uri = f"gs://{bucket_name}/{file_name}"
        blob_source = vision.Image(source=vision.ImageSource(image_uri=blob_uri))
    
        # Ignore already-blurred files
        if file_name.startswith("blurred-"):
            print(f"The image {file_name} is already blurred.")
            return
    
        print(f"Analyzing {file_name}.")
    
        result = vision_client.safe_search_detection(image=blob_source)
        detected = result.safe_search_annotation
    
        # Process image
        if detected.adult == 5 or detected.violence == 5:
            print(f"The image {file_name} was detected as inappropriate.")
            return __blur_image(blob)
        else:
            print(f"The image {file_name} was detected as OK.")
    
    

    המשך

    
    // GCSEvent is the payload of a GCS event.
    type GCSEvent struct {
    	Bucket string `json:"bucket"`
    	Name   string `json:"name"`
    }
    
    // BlurOffensiveImages blurs offensive images uploaded to GCS.
    func BlurOffensiveImages(ctx context.Context, e GCSEvent) error {
    	outputBucket := os.Getenv("BLURRED_BUCKET_NAME")
    	if outputBucket == "" {
    		return errors.New("BLURRED_BUCKET_NAME must be set")
    	}
    
    	img := vision.NewImageFromURI(fmt.Sprintf("gs://%s/%s", e.Bucket, e.Name))
    
    	resp, err := visionClient.DetectSafeSearch(ctx, img, nil)
    	if err != nil {
    		return fmt.Errorf("AnnotateImage: %w", err)
    	}
    
    	if resp.GetAdult() == visionpb.Likelihood_VERY_LIKELY ||
    		resp.GetViolence() == visionpb.Likelihood_VERY_LIKELY {
    		return blur(ctx, e.Bucket, outputBucket, e.Name)
    	}
    	log.Printf("The image %q was detected as OK.", e.Name)
    	return nil
    }
    

    Java

    // Blurs uploaded images that are flagged as Adult or Violence.
    public static void blurOffensiveImages(JsonObject data) {
      String fileName = data.get("name").getAsString();
      String bucketName = data.get("bucket").getAsString();
      BlobInfo blobInfo = BlobInfo.newBuilder(bucketName, fileName).build();
      // Construct URI to GCS bucket and file.
      String gcsPath = String.format("gs://%s/%s", bucketName, fileName);
      System.out.println(String.format("Analyzing %s", fileName));
    
      // Construct request.
      List<AnnotateImageRequest> requests = new ArrayList<>();
      ImageSource imgSource = ImageSource.newBuilder().setImageUri(gcsPath).build();
      Image img = Image.newBuilder().setSource(imgSource).build();
      Feature feature = Feature.newBuilder().setType(Type.SAFE_SEARCH_DETECTION).build();
      AnnotateImageRequest request =
          AnnotateImageRequest.newBuilder().addFeatures(feature).setImage(img).build();
      requests.add(request);
    
      // Send request to the Vision API.
      try (ImageAnnotatorClient client = ImageAnnotatorClient.create()) {
        BatchAnnotateImagesResponse response = client.batchAnnotateImages(requests);
        List<AnnotateImageResponse> responses = response.getResponsesList();
        for (AnnotateImageResponse res : responses) {
          if (res.hasError()) {
            System.out.println(String.format("Error: %s\n", res.getError().getMessage()));
            return;
          }
          // Get Safe Search Annotations
          SafeSearchAnnotation annotation = res.getSafeSearchAnnotation();
          if (annotation.getAdultValue() == 5 || annotation.getViolenceValue() == 5) {
            System.out.println(String.format("Detected %s as inappropriate.", fileName));
            blur(blobInfo);
          } else {
            System.out.println(String.format("Detected %s as OK.", fileName));
          }
        }
      } catch (Exception e) {
        System.out.println(String.format("Error with Vision API: %s", e.getMessage()));
      }
    }

  4. מאחזרים את התמונה שאליה מתייחסים מקטגוריית הקלט של Cloud Storage שנוצרה למעלה, משתמשים ב-ImageMagick כדי להחיל על התמונה אפקט של טשטוש ומעלים את התוצאה לקטגוריית הפלט.

    Node.js

    // Blurs the given file using sharp, and uploads it to another bucket.
    const blurImage = async (file, blurredBucketName) => {
      const tempLocalPath = `/tmp/${path.parse(file.name).base}`;
      const tempLocalBlurredPath = `/tmp/blurred-${path.parse(file.name).base}`;
    
      // Download file from bucket.
      try {
        await file.download({destination: tempLocalPath});
    
        console.log(`Downloaded ${file.name} to ${tempLocalPath}.`);
      } catch (err) {
        throw new Error(`File download failed: ${err}`);
      }
    
      try {
        await sharp(tempLocalPath).blur(16).toFile(tempLocalBlurredPath);
    
        console.log(`Blurred image: ${file.name}`);
      } catch (err) {
        console.error('Failed to blur image.', err);
        throw err;
      }
    
      // Upload result to a different bucket, to avoid re-triggering this function.
      const blurredBucket = storage.bucket(blurredBucketName);
    
      // Upload the Blurred image back into the bucket.
      const gcsPath = `gs://${blurredBucketName}/${file.name}`;
      try {
        await blurredBucket.upload(tempLocalBlurredPath, {destination: file.name});
        console.log(`Uploaded blurred image to: ${gcsPath}`);
      } catch (err) {
        throw new Error(`Unable to upload blurred image to ${gcsPath}: ${err}`);
      } finally {
        // Delete the temporary file.
        await Promise.allSettled([
          fs.unlink(tempLocalPath),
          fs.unlink(tempLocalBlurredPath),
        ]);
      }
    };

    Python

    def __blur_image(current_blob):
        """Blurs the given file using ImageMagick.
    
        Args:
            current_blob: a Cloud Storage blob
        """
        file_name = current_blob.name
        _, temp_local_filename = tempfile.mkstemp()
    
        # Download file from bucket.
        current_blob.download_to_filename(temp_local_filename)
        print(f"Image {file_name} was downloaded to {temp_local_filename}.")
    
        # Blur the image using ImageMagick.
        with Image(filename=temp_local_filename) as image:
            image.resize(*image.size, blur=16, filter="hamming")
            image.save(filename=temp_local_filename)
    
        print(f"Image {file_name} was blurred.")
    
        # Upload result to a second bucket, to avoid re-triggering the function.
        # You could instead re-upload it to the same bucket + tell your function
        # to ignore files marked as blurred (e.g. those with a "blurred" prefix)
        blur_bucket_name = os.getenv("BLURRED_BUCKET_NAME")
        blur_bucket = storage_client.bucket(blur_bucket_name)
        new_blob = blur_bucket.blob(file_name)
        new_blob.upload_from_filename(temp_local_filename)
        print(f"Blurred image uploaded to: gs://{blur_bucket_name}/{file_name}")
    
        # Delete the temporary file.
        os.remove(temp_local_filename)
    
    

    המשך

    
    // blur blurs the image stored at gs://inputBucket/name and stores the result in
    // gs://outputBucket/name.
    func blur(ctx context.Context, inputBucket, outputBucket, name string) error {
    	inputBlob := storageClient.Bucket(inputBucket).Object(name)
    	r, err := inputBlob.NewReader(ctx)
    	if err != nil {
    		return fmt.Errorf("NewReader: %w", err)
    	}
    
    	outputBlob := storageClient.Bucket(outputBucket).Object(name)
    	w := outputBlob.NewWriter(ctx)
    	defer w.Close()
    
    	// Use - as input and output to use stdin and stdout.
    	cmd := exec.Command("convert", "-", "-blur", "0x8", "-")
    	cmd.Stdin = r
    	cmd.Stdout = w
    
    	if err := cmd.Run(); err != nil {
    		return fmt.Errorf("cmd.Run: %w", err)
    	}
    
    	log.Printf("Blurred image uploaded to gs://%s/%s", outputBlob.BucketName(), outputBlob.ObjectName())
    
    	return nil
    }
    

    Java

      // Blurs the file described by blobInfo using ImageMagick,
      // and uploads it to the blurred bucket.
      public static void blur(BlobInfo blobInfo) throws IOException {
        String bucketName = blobInfo.getBucket();
        String fileName = blobInfo.getName();
        // Download image
        Blob blob = storage.get(BlobId.of(bucketName, fileName));
        Path download = Paths.get("/tmp/", fileName);
        blob.downloadTo(download);
    
        // Construct the command.
        List<String> args = new ArrayList<>();
        args.add("convert");
        args.add(download.toString());
        args.add("-blur");
        args.add("0x8");
        Path upload = Paths.get("/tmp/", "blurred-" + fileName);
        args.add(upload.toString());
        try {
          ProcessBuilder pb = new ProcessBuilder(args);
          Process process = pb.start();
          process.waitFor();
        } catch (Exception e) {
          System.out.println(String.format("Error: %s", e.getMessage()));
        }
    
        // Upload image to blurred bucket.
        BlobId blurredBlobId = BlobId.of(BLURRED_BUCKET_NAME, fileName);
        BlobInfo blurredBlobInfo =
            BlobInfo.newBuilder(blurredBlobId).setContentType(blob.getContentType()).build();
        try {
          byte[] blurredFile = Files.readAllBytes(upload);
          Blob blurredBlob = storage.create(blurredBlobInfo, blurredFile);
          System.out.println(
              String.format("Blurred image uploaded to: gs://%s/%s", BLURRED_BUCKET_NAME, fileName));
        } catch (Exception e) {
          System.out.println(String.format("Error in upload: %s", e.getMessage()));
        }
    
        // Remove images from fileSystem
        Files.delete(download);
        Files.delete(upload);
      }
    }

שילוב עיבוד תמונות בקוד לדוגמה של Pub/Sub

כדי לשנות את השירות הקיים כך שיכלול את קוד עיבוד התמונות:

  1. מוסיפים תלות חדשה לשירות, כולל ספריות הלקוח של Cloud Vision ו-Cloud Storage:

    Node.js

    npm install gm @google-cloud/storage @google-cloud/vision

    Python

    מוסיפים את ספריות הלקוח הנדרשות כך שקובץ requirements.txt ייראה בערך כך:
    Flask==3.1.3; python_version >= '3.9'
    google-cloud-storage==2.12.0
    google-cloud-vision==3.8.1
    gunicorn==23.0.0
    Wand==0.6.13
    Werkzeug==3.1.8; python_version >= '3.9'
    

    המשך

    אפליקציית הדוגמה של Go משתמשת במודולים של Go. יחסי התלות החדשים שנוספו למעלה בהצהרת הייבוא imagemagick/imagemagick.go יורדו באופן אוטומטי על ידי הפקודה הבאה שזקוקה להם.

    Java

    מוסיפים את התלות הבאה בקטע <dependencyManagement> בקובץ pom.xml:
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>spring-cloud-gcp-dependencies</artifactId>
      <version>4.9.2</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    
    מוסיפים את יחסי התלות הבאים בקטע <dependencies> בקובץ pom.xml:
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>spring-cloud-gcp-starter-vision</artifactId>
    </dependency>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>spring-cloud-gcp-starter-storage</artifactId>
    </dependency>
    

  2. מוסיפים את חבילת המערכת ImageMagick לקונטיינר על ידי שינוי Dockerfile מתחת להצהרה FROM. אם משתמשים ב-Dockerfile 'רב-שלבי', צריך למקם את הפקודה הזו בשלב הסופי.

    Debian/Ubuntu
    
    # Install Imagemagick into the container image.
    # For more on system packages review the system packages tutorial.
    # https://cloud.google.com/run/docs/tutorials/system-packages#dockerfile
    RUN set -ex; \
      apt-get -y update; \
      apt-get -y install imagemagick; \
      rm -rf /var/lib/apt/lists/*
    
    Alpine
    
    # Install Imagemagick into the container image.
    # For more on system packages review the system packages tutorial.
    # https://cloud.google.com/run/docs/tutorials/system-packages#dockerfile
    RUN apk add --no-cache imagemagick
    

    מידע נוסף על עבודה עם חבילות מערכת בשירות Cloud Run זמין במדריך לשימוש בחבילות מערכת.

  3. מחליפים את הקוד הקיים לטיפול בהודעות Pub/Sub בקריאה לפונקציה של לוגיקת הטשטוש החדשה.

    Node.js

    קובץ app.js מגדיר את אפליקציית Express.js ומכין את ההודעות שהתקבלו מ-Pub/Sub לשימוש. צריך לבצע את השינויים הבאים:

    • הוספת קוד לייבוא קובץ image.js חדש
    • מסירים את הקוד הקיים 'Hello World' מהנתיב
    • הוספת קוד כדי לאמת עוד יותר את הודעת Pub/Sub
    • הוספת קוד להפעלת הפונקציה החדשה לעיבוד תמונות

      בסיום, הקוד ייראה כך:

    
    const express = require('express');
    const app = express();
    
    // This middleware is available in Express v4.16.0 onwards
    app.use(express.json());
    
    const image = require('./image');
    
    app.post('/', async (req, res) => {
      if (!req.body) {
        const msg = 'no Pub/Sub message received';
        console.error(`error: ${msg}`);
        res.status(400).send(`Bad Request: ${msg}`);
        return;
      }
      if (!req.body.message || !req.body.message.data) {
        const msg = 'invalid Pub/Sub message format';
        console.error(`error: ${msg}`);
        res.status(400).send(`Bad Request: ${msg}`);
        return;
      }
    
      // Decode the Pub/Sub message.
      const pubSubMessage = req.body.message;
      let data;
      try {
        data = Buffer.from(pubSubMessage.data, 'base64').toString().trim();
        data = JSON.parse(data);
      } catch (err) {
        const msg =
          'Invalid Pub/Sub message: data property is not valid base64 encoded JSON';
        console.error(`error: ${msg}: ${err}`);
        res.status(400).send(`Bad Request: ${msg}`);
        return;
      }
    
      // Validate the message is a Cloud Storage event.
      if (!data.name || !data.bucket) {
        const msg =
          'invalid Cloud Storage notification: expected name and bucket properties';
        console.error(`error: ${msg}`);
        res.status(400).send(`Bad Request: ${msg}`);
        return;
      }
    
      try {
        await image.blurOffensiveImages(data);
        res.status(204).send();
      } catch (err) {
        console.error(`error: Blurring image: ${err}`);
        res.status(500).send();
      }
    });

    Python

    קובץ main.py מגדיר את אפליקציית Flask ומכין את ההודעות שהתקבלו מ-Pub/Sub לשימוש. צריך לבצע את השינויים הבאים:

    • הוספת קוד לייבוא קובץ image.py חדש
    • מסירים את הקוד הקיים 'Hello World' מהנתיב
    • הוספת קוד כדי לאמת עוד יותר את הודעת Pub/Sub
    • הוספת קוד להפעלת הפונקציה החדשה לעיבוד תמונות

      בסיום, הקוד ייראה כך:

    import base64
    import json
    import os
    
    from flask import Flask, request
    
    import image
    
    
    app = Flask(__name__)
    
    
    @app.route("/", methods=["POST"])
    def index():
        """Receive and parse Pub/Sub messages containing Cloud Storage event data."""
        envelope = request.get_json()
        if not envelope:
            msg = "no Pub/Sub message received"
            print(f"error: {msg}")
            return f"Bad Request: {msg}", 400
    
        if not isinstance(envelope, dict) or "message" not in envelope:
            msg = "invalid Pub/Sub message format"
            print(f"error: {msg}")
            return f"Bad Request: {msg}", 400
    
        # Decode the Pub/Sub message.
        pubsub_message = envelope["message"]
    
        if isinstance(pubsub_message, dict) and "data" in pubsub_message:
            try:
                data = json.loads(base64.b64decode(pubsub_message["data"]).decode())
    
            except Exception as e:
                msg = (
                    "Invalid Pub/Sub message: "
                    "data property is not valid base64 encoded JSON"
                )
                print(f"error: {e}")
                return f"Bad Request: {msg}", 400
    
            # Validate the message is a Cloud Storage event.
            if not data["name"] or not data["bucket"]:
                msg = (
                    "Invalid Cloud Storage notification: "
                    "expected name and bucket properties"
                )
                print(f"error: {msg}")
                return f"Bad Request: {msg}", 400
    
            try:
                image.blur_offensive_images(data)
                return ("", 204)
    
            except Exception as e:
                print(f"error: {e}")
                return ("", 500)
    
        return ("", 500)
    

    המשך

    קובץ main.go מגדיר את שירות ה-HTTP ומכין את ההודעות שהתקבלו מ-Pub/Sub לשימוש. צריך לבצע את השינויים הבאים:

    • הוספת קוד לייבוא קובץ imagemagick.go חדש
    • מסירים את הקוד הקיים של Hello World מה-handler.
    • הוספת קוד כדי לאמת עוד יותר את הודעת Pub/Sub
    • הוספת קוד להפעלת הפונקציה החדשה לעיבוד תמונות

    
    // Sample image-processing is a Cloud Run service which performs asynchronous processing on images.
    package main
    
    import (
    	"encoding/json"
    	"io"
    	"log"
    	"net/http"
    	"os"
    
    	"github.com/GoogleCloudPlatform/golang-samples/run/image-processing/imagemagick"
    )
    
    func main() {
    	http.HandleFunc("/", HelloPubSub)
    	// Determine port for HTTP service.
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    	}
    	// Start HTTP server.
    	log.Printf("Listening on port %s", port)
    	if err := http.ListenAndServe(":"+port, nil); err != nil {
    		log.Fatal(err)
    	}
    }
    
    // PubSubMessage is the payload of a Pub/Sub event.
    // See the documentation for more details:
    // https://cloud.google.com/pubsub/docs/reference/rest/v1/PubsubMessage
    type PubSubMessage struct {
    	Message struct {
    		Data []byte `json:"data,omitempty"`
    		ID   string `json:"id"`
    	} `json:"message"`
    	Subscription string `json:"subscription"`
    }
    
    // HelloPubSub receives and processes a Pub/Sub push message.
    func HelloPubSub(w http.ResponseWriter, r *http.Request) {
    	var m PubSubMessage
    	body, err := io.ReadAll(r.Body)
    	if err != nil {
    		log.Printf("ioutil.ReadAll: %v", err)
    		http.Error(w, "Bad Request", http.StatusBadRequest)
    		return
    	}
    	if err := json.Unmarshal(body, &m); err != nil {
    		log.Printf("json.Unmarshal: %v", err)
    		http.Error(w, "Bad Request", http.StatusBadRequest)
    		return
    	}
    
    	var e imagemagick.GCSEvent
    	if err := json.Unmarshal(m.Message.Data, &e); err != nil {
    		log.Printf("json.Unmarshal: %v", err)
    		http.Error(w, "Bad Request", http.StatusBadRequest)
    		return
    	}
    
    	if e.Name == "" || e.Bucket == "" {
    		log.Printf("invalid GCSEvent: expected name and bucket")
    		http.Error(w, "Bad Request", http.StatusBadRequest)
    		return
    	}
    
    	if err := imagemagick.BlurOffensiveImages(r.Context(), e); err != nil {
    		log.Printf("imagemagick.BlurOffensiveImages: %v", err)
    		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
    	}
    }
    

    Java

    קובץ PubSubController.java מגדיר את בקר הנתונים שמטפל בבקשות HTTP ומכין את ההודעות שהתקבלו מ-Pub/Sub לשימוש. צריך לבצע את השינויים הבאים:

    • הוספת הייבוא החדש
    • מסירים את הקוד הקיים 'Hello World' מהבקר
    • הוספת קוד כדי לאמת עוד יותר את הודעת Pub/Sub
    • הוספת קוד להפעלת הפונקציה החדשה לעיבוד תמונות

    import com.google.gson.JsonObject;
    import com.google.gson.JsonParser;
    import java.util.Base64;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    // PubsubController consumes a Pub/Sub message.
    @RestController
    public class PubSubController {
      @RequestMapping(value = "/", method = RequestMethod.POST)
      public ResponseEntity<String> receiveMessage(@RequestBody Body body) {
        // Get PubSub message from request body.
        Body.Message message = body.getMessage();
        if (message == null) {
          String msg = "Bad Request: invalid Pub/Sub message format";
          System.out.println(msg);
          return new ResponseEntity<>(msg, HttpStatus.BAD_REQUEST);
        }
    
        // Decode the Pub/Sub message.
        String pubSubMessage = message.getData();
        JsonObject data;
        try {
          String decodedMessage = new String(Base64.getDecoder().decode(pubSubMessage));
          data = JsonParser.parseString(decodedMessage).getAsJsonObject();
        } catch (Exception e) {
          String msg = "Error: Invalid Pub/Sub message: data property is not valid base64 encoded JSON";
          System.out.println(msg);
          return new ResponseEntity<>(msg, HttpStatus.BAD_REQUEST);
        }
    
        // Validate the message is a Cloud Storage event.
        if (data.get("name") == null || data.get("bucket") == null) {
          String msg = "Error: Invalid Cloud Storage notification: expected name and bucket properties";
          System.out.println(msg);
          return new ResponseEntity<>(msg, HttpStatus.BAD_REQUEST);
        }
    
        try {
          ImageMagick.blurOffensiveImages(data);
        } catch (Exception e) {
          String msg = String.format("Error: Blurring image: %s", e.getMessage());
          System.out.println(msg);
          return new ResponseEntity<>(msg, HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return new ResponseEntity<>(HttpStatus.OK);
      }
    }

הורדת הדוגמה המלאה

כדי לאחזר את דוגמת הקוד המלאה של עיבוד תמונה לשימוש:

  1. משכפלים את מאגר האפליקציה לדוגמה ומעבירים אותו למכונה המקומית:

    Node.js

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

    אפשרות נוספת היא להוריד את הדוגמה כקובץ ZIP ולחלץ אותה.

    Python

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    אפשרות נוספת היא להוריד את הדוגמה כקובץ ZIP ולחלץ אותה.

    המשך

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git

    אפשרות נוספת היא להוריד את הדוגמה כקובץ ZIP ולחלץ אותה.

    Java

    git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git

    אפשרות נוספת היא להוריד את הדוגמה כקובץ ZIP ולחלץ אותה.

  2. עוברים לספרייה שמכילה את הקוד לדוגמה של Cloud Run:

    Node.js

    cd nodejs-docs-samples/run/image-processing/

    Python

    cd python-docs-samples/run/image-processing/

    Go

    cd golang-samples/run/image-processing/

    Java

    cd java-docs-samples/run/image-processing/

שליחת הקוד

תהליך שליחת הקוד כולל שלושה שלבים: יצירת קובץ אימג' של קונטיינר באמצעות Cloud Build, העלאת קובץ האימג' של הקונטיינר ל-Artifact Registry ופריסת קובץ האימג' של הקונטיינר ל-Cloud Run.

כדי לשלוח את הקוד:

  1. יוצרים את הקונטיינר ומפרסמים אותו ב-Artifact Registry:

    Node.js

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID
    /REPOSITORY/pubsub

    כאשר pubsub הוא שם השירות.

    מחליפים את:

    • PROJECT_ID במזהה הפרויקט ב- Google Cloud
    • REPOSITORY בשם המאגר ב-Artifact Registry.
    • REGION עם האזור שבו ישמש מאגר Artifact Registry. Google Cloud

    אם הפעולה תצליח, תופיע הודעה עם המזהה, זמן היצירה ושם התמונה. התמונה מאוחסנת ב-Artifact Registry ואפשר לעשות בה שימוש חוזר אם צריך.

    Python

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID
    /REPOSITORY/pubsub

    כאשר pubsub הוא שם השירות.

    מחליפים את:

    • PROJECT_ID במזהה הפרויקט ב- Google Cloud
    • REPOSITORY בשם המאגר ב-Artifact Registry.
    • REGION עם האזור שבו ישמש מאגר Artifact Registry. Google Cloud

    אם הפעולה תצליח, תופיע הודעה עם המזהה, זמן היצירה ושם התמונה. התמונה מאוחסנת ב-Artifact Registry ואפשר לעשות בה שימוש חוזר אם צריך.

    המשך

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID
    /REPOSITORY/pubsub

    כאשר pubsub הוא שם השירות.

    מחליפים את:

    • PROJECT_ID במזהה הפרויקט ב- Google Cloud
    • REPOSITORY בשם המאגר ב-Artifact Registry.
    • REGION עם האזור שבו ישמש מאגר Artifact Registry. Google Cloud

    אם הפעולה תצליח, תופיע הודעה עם המזהה, זמן היצירה ושם התמונה. התמונה מאוחסנת ב-Artifact Registry ואפשר לעשות בה שימוש חוזר אם צריך.

    Java

    בדוגמה הזו נעשה שימוש ב-Jib כדי ליצור תמונות Docker באמצעות כלים נפוצים של Java. ‫Jib מבצע אופטימיזציה של בניית קונטיינרים בלי צורך בקובץ Dockerfile או בהתקנה של Docker. מידע נוסף על יצירת מאגרי Java באמצעות Jib

    1. באמצעות קובץ Docker, מגדירים ויוצרים תמונת בסיס עם חבילות המערכת שהותקנו כדי לבטל את תמונת הבסיס שמוגדרת כברירת מחדל ב-Jib:

      # Use eclipse-temurin for base image.
      # It's important to use JDK 8u191 or above that has container support enabled.
      # https://hub.docker.com/_/eclipse-temurin/
      # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
      FROM eclipse-temurin:17.0.18_8-jre
      
      # Install Imagemagick into the container image.
      # For more on system packages review the system packages tutorial.
      # https://cloud.google.com/run/docs/tutorials/system-packages#dockerfile
      RUN set -ex; \
        apt-get -y update; \
        apt-get -y install imagemagick; \
        rm -rf /var/lib/apt/lists/*

      gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID
      /REPOSITORY/imagemagick

      מחליפים את:

      • PROJECT_ID במזהה הפרויקט ב- Google Cloud
      • REPOSITORY בשם המאגר ב-Artifact Registry.
      • REGION עם האזור שבו ישמש מאגר Artifact Registry. Google Cloud
    2. משתמשים בכלי העזר לפרטי כניסה של gcloud כדי לתת ל-Docker הרשאה להעביר בדחיפה אל Artifact Registry.

      gcloud auth configure-docker

    3. יוצרים את מאגר התגים הסופי באמצעות Jib ומפרסמים אותו ב-Artifact Registry:

      <plugin>
        <groupId>com.google.cloud.tools</groupId>
        <artifactId>jib-maven-plugin</artifactId>
        <version>3.4.0</version>
        <configuration>
          <from>
            <image>gcr.io/PROJECT_ID/imagemagick</image>
          </from>
          <to>
            <image>gcr.io/PROJECT_ID/pubsub</image>
          </to>
        </configuration>
      </plugin>
      
      mvn compile jib:build \
        -Dimage=REGION-docker.pkg.dev/PROJECT_ID
      /REPOSITORY/pubsub \
        -Djib.from.image=REGION-docker.pkg.dev/PROJECT_ID
      /REPOSITORY/imagemagick

      מחליפים את:

      • PROJECT_ID במזהה הפרויקט ב- Google Cloud
      • REPOSITORY בשם המאגר ב-Artifact Registry.
      • REGION עם האזור שבו ישמש מאגר Artifact Registry. Google Cloud

  2. מריצים את הפקודה הבאה כדי לפרוס את השירות, ומשתמשים באותו שם שירות שבו השתמשתם במדריך לשימוש ב-Pub/Sub:

    Node.js

    gcloud run deploy pubsub-tutorial --image REGION-docker.pkg.dev/PROJECT_ID
    /REPOSITORY/pubsub --set-env-vars=BLURRED_BUCKET_NAME=BLURRED_BUCKET_NAME --no-allow-unauthenticated

    Python

    gcloud run deploy pubsub-tutorial --image REGION-docker.pkg.dev/PROJECT_ID
    /REPOSITORY/pubsub --set-env-vars=BLURRED_BUCKET_NAME=BLURRED_BUCKET_NAME --no-allow-unauthenticated

    Go

    gcloud run deploy pubsub-tutorial --image REGION-docker.pkg.dev/PROJECT_ID
    /REPOSITORY/pubsub --set-env-vars=BLURRED_BUCKET_NAME=BLURRED_BUCKET_NAME --no-allow-unauthenticated

    Java

    gcloud run deploy pubsub-tutorial --image REGION-docker.pkg.dev/PROJECT_ID
    /REPOSITORY/pubsub --set-env-vars=BLURRED_BUCKET_NAME=BLURRED_BUCKET_NAME --memory 512M --no-allow-unauthenticated

    כאשר pubsub הוא שם הקונטיינר ו-pubsub-tutorial הוא שם השירות. שימו לב שקובץ האימג' של הקונטיינר נפרס בשירות ובאזור (Cloud Run) שהגדרתם קודם בקטע הגדרת ברירות מחדל ב-gcloud. מחליפים את:

    • PROJECT_ID במזהה הפרויקט ב- Google Cloud
    • REPOSITORY בשם המאגר ב-Artifact Registry.
    • REGION עם האזור שבו ישמש מאגר Artifact Registry. Google Cloud
    • BLURRED_BUCKET_NAME עם קטגוריה של Cloud Storage שיצרתם קודם כדי לקבל תמונות מטושטשות, כדי להגדיר את משתנה הסביבה.

    הדגל --no-allow-unauthenticated מגביל גישה לא מאומתת לשירות. אם השירות פרטי, אפשר להסתמך על השילוב האוטומטי של Cloud Run עם Pub/Sub כדי לאמת בקשות. פרטים נוספים על ההגדרה מופיעים במאמר בנושא שילוב עם Pub/Sub. פרטים נוספים על אימות מבוסס-IAM זמינים במאמר ניהול גישה.

    מחכים עד שהפריסה תושלם. התהליך הזה יכול להימשך כחצי דקה. אם הפעולה בוצעה ללא שגיאות, כתובת ה-URL של השירות מוצגת בשורת הפקודה.

הפעלת ההתראות מ-Cloud Storage

הגדרת Cloud Storage לפרסום הודעה בנושא Pub/Sub בכל פעם שקובץ (שנקרא אובייקט) מועלה או משתנה. שולחים את ההתראה לנושא שנוצר קודם, כך שכל העלאה חדשה של קובץ תפעיל את השירות.

gcloud

gcloud storage service-agent --project=PROJECT_ID
gcloud storage buckets notifications create gs://INPUT_BUCKET_NAME --topic=myRunTopic --payload-format=json

myRunTopic הוא הנושא שיצרתם במדריך הקודם.

מחליפים את INPUT_BUCKET_NAME בשם שבו השתמשתם כשיצרתם את הקטגוריות.

לפרטים נוספים על התראות של קטגוריות אחסון, ראו הגדרת התראות Pub/Sub ל-Cloud Storage.

Terraform

כדי ללמוד איך להחיל הגדרות ב-Terraform או להסיר אותן, ראו פקודות בסיסיות ב-Terraform.

כדי להפעיל התראות, צריך שיהיה קיים חשבון שירות של Cloud Storage שייחודי לפרויקט, וצריכה להיות לו הרשאת IAM‏ pubsub.publisher בנושא Pub/Sub. כדי להעניק את ההרשאה הזו וליצור התראה ב-Cloud Storage, מוסיפים את השורות הבאות לקובץ main.tf הקיים:

data "google_storage_project_service_account" "gcs_account" {}

resource "google_pubsub_topic_iam_binding" "binding" {
  topic   = google_pubsub_topic.default.name
  role    = "roles/pubsub.publisher"
  members = ["serviceAccount:${data.google_storage_project_service_account.gcs_account.email_address}"]
}

resource "google_storage_notification" "notification" {
  bucket         = google_storage_bucket.imageproc_input.name
  payload_format = "JSON_API_V1"
  topic          = google_pubsub_topic.default.id
  depends_on     = [google_pubsub_topic_iam_binding.binding]
}

רוצה לנסות?

  1. העלאת תמונה פוגענית, כמו התמונה הזו של זומבי שאוכל בשר:

    curl -o zombie.jpg https://cdn.pixabay.com/photo/2015/09/21/14/24/zombie-949916_960_720.jpg
    gcloud storage cp zombie.jpg gs://INPUT_BUCKET_NAME

    כאשר INPUT_BUCKET_NAME היא קטגוריה של Cloud Storage שיצרתם קודם להעלאת תמונות.

  2. עוברים ליומני השירות:

    1. נכנסים לדף Cloud Run ב Google Cloud מסוף.
    2. לוחצים על השירות pubsub-tutorial.
    3. לוחצים על הכרטיסייה יומנים. יכול להיות שייקח ליומנים כמה דקות להופיע. אם הם לא מופיעים מיד, כדאי לבדוק שוב אחרי כמה רגעים.
  3. מחפשים את ההודעה Blurred image: zombie.png.

  4. אפשר לראות את התמונות המטושטשות ב-BLURRED_BUCKET_NAME Cloud Storage bucket שיצרתם קודם: מאתרים את ה-bucket בדף Cloud Storage ב Google Cloud מסוף

הסרת המשאבים

כדי להימנע מחיובים נוספים בחשבון Google Cloud , מוחקים את כל המשאבים שהצבתם באמצעות המדריך הזה.

מחיקת הפרויקט

אם יצרתם פרויקט חדש בשביל המדריך הזה, מוחקים את הפרויקט. אם השתמשתם בפרויקט קיים ואתם רוצים לשמור אותו בלי השינויים שהוספתם במדריך הזה, תצטרכו למחוק את המשאבים שיצרתם לצורך המדריך.

הדרך הקלה ביותר לבטל את החיוב היא למחוק את הפרויקט שיצרתם בשביל המדריך.

כדי למחוק את הפרויקט:

  1. במסוף Google Cloud , נכנסים לדף Manage resources.

    כניסה לדף Manage resources

  2. ברשימת הפרויקטים, בוחרים את הפרויקט שרוצים למחוק ולוחצים על Delete.
  3. כדי למחוק את הפרויקט, כותבים את מזהה הפרויקט בתיבת הדו-שיח ולוחצים על Shut down.

מחיקת משאבי הדרכה

  1. מוחקים את שירות Cloud Run שפרסתם במדריך הזה. שירותי Cloud Run לא צוברים עלויות עד שהם מקבלים בקשות.

    כדי למחוק את שירות Cloud Run, מריצים את הפקודה הבאה:

    gcloud run services delete SERVICE-NAME

    מחליפים את SERVICE-NAME בשם השירות.

    אפשר גם למחוק שירותים של Cloud Run מGoogle Cloud המסוף.

  2. מסירים את הגדרת ברירת המחדל של האזור gcloud שהוספתם במהלך ההגדרה של המדריך:

     gcloud config unset run/region
    
  3. מסירים את הגדרות הפרויקט:

     gcloud config unset project
    
  4. מחיקת משאבים אחרים Google Cloud שנוצרו במדריך הזה:

המאמרים הבאים

  • מידע נוסף על שמירת נתונים ב-Cloud Run באמצעות Cloud Storage
  • להבין איך להשתמש ב-Cloud Vision API כדי לזהות דברים חוץ מתוכן בוטה.
  • כדאי להעמיק את הקריאה ולהכיר דוגמאות לארכיטקטורות, תרשימים ושיטות מומלצות בנושאי Google Cloud. כל אלה זמינים במרכז הארכיטקטורה של Cloud.