【Rails】Active Storage + S3 + Active JobでGoogle Cloud Visionセーフサーチ

個人開発のWebアプリまちかどルートv6.0rc6への実装メモです。

Amazon S3に画像をダイレクトアップロード

Railsガイドをもとに実装しました。

セーフサーチを実装

前回の記事のとおりです。

【Rails】Active Storage環境下でGoogle Cloud Visionのセーフサーチを実装

S3アップロード画像のパブリックURLを取得

こちらの記事を参考にしました。

Rails ActiveStorage で PUBLIC な URL を表示する

セーフサーチを非同期処理化

※controllerのコードは省略します

model

    has_one_attached :image  
    after_commit :annotate_self, on: [:create, :update]  

    def annotate_self  
        if image.attached?  
            ImageAnnotateJob.set(wait: 10.second).perform_later(self)  
        end  
    end  

postの新規投稿もしくは編集のときに画像imageが添付されていたらImageAnnotateJobというActive Jobのキューを走らせます。10秒後に走らすようセットした理由は、Amazon S3へのアップロード完了までのタイムラグが発生するかなと思ったからです。

job

class ImageAnnotateJob < ApplicationJob  
  queue_as :second  

  def perform(target)  

    tempfile = target.image.attachment.service.send(:object_for, target.image.key).public_url  

    require "google/cloud/vision"  
    image_annotator = Google::Cloud::Vision::ImageAnnotator.new  
    response = image_annotator.safe_search_detection image: tempfile  
    response.responses.each do |res|  
    safe_search = res.safe_search_annotation  
      if safe_search.adult.to_s == "VERY_LIKELY" || safe_search.adult.to_s == "LIKELY"  
        target.destroy  
        return  
      elsif safe_search.violence.to_s == "VERY_LIKELY" || safe_search.violence.to_s == "LIKELY"  
        target.destroy  
        return  
      elsif safe_search.medical.to_s == "VERY_LIKELY" || safe_search.medical.to_s == "LIKELY"  
        target.destroy  
        return  
      end  
    end  

  end  

end  

いったんtempfileにS3アップロード画像のパブリックURLを格納。それをGoogle Cloud Visionのセーフサーチにかけます。このコードはすべてActive Jobによってバックグラウンド(非同期)処理されます。もし不適切な画像と判断されれば投稿そのものを削除する流れです。

Sidekiq

:concurrency: 5  
:queues:  
  - [default, 7]  
  - [second, 5]  

じぶんはActive JobのライブラリとしてSidekiqを使用しています。上記image_annotate_job.rbqueue_as :secondによってキューが処理される優先順位を設定しました。

あとがき

駆け足ですが以上です。Google Cloud Visionのセーフサーチは高精度ですがレスポンスに1~2秒ほど要するため、そのぶん前回の記事のような同期処理だと投稿完了までにユーザーを待たせてしまううえ、サーバーサイドに負荷がかかります。今回の記事はそれらの課題を解決するものとなります。

また、苦労したのがS3アップロード画像のパブリックURLを取得する部分でした。url_forrails_blob_urlといったActive Storageのメソッドを使うと時限的なものorリダイレクトされるURLしか取得できずセーフサーチにかけても「そんな画像はありませんよ」と言われてしまいます。

というわけで試行錯誤を経たおかげでよりレスポンス性とセキュリティ性を兼ね備えた画像投稿機能を実装できました。