Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

【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

post.rb
    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

image_annotate_job.rb
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

config\sidekiq.yml
: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しか取得できずセーフサーチにかけても「そんな画像はありませんよ」と言われてしまいます。

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

補足: Rails 6.0ではこちらもご覧ください
https://qiita.com/west2538/items/f932978e79220b880693

gemのv1.0.0に対応した記事を書きました
https://qiita.com/west2538/items/89bdbc4a935d12a65202

west2538
40代でセミリタイアして中山間地域の豪雪地帯に新しい住まいの形「ギルドハウス十日町」を設立。そこでWebサービスなどを開発しています
https://machikado-guild.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away