Ruby on RailsからGoogle Driveにファイルをアップロードしてみた(表示&削除も)


はじめに

先日、Railsで作成した業務管理系ツールにて、「フォームからファイルをアップロードするとGoogleDriveの指定したディレクトリ内に格納される」という機能を実装しました。GoogleDriveAPIは初見だったのですが、公式のリファレンスをはじめ英語の情報ばかりで困ったのと割と古めの情報が多かったので実装した内容を記事として残します。

今回説明しないこと

準備

  • Railsから使用するアップロード用のモデルImageと対応するimagesコントローラーを作成
    • カラムはfileのみ
  • ビューはslimで記載しているため必要に応じて読み替える
  • 今回使用する全ての機能で必要となるGoogle Drive APIを使うための設定(API有効化と認証方法)をapplication_contoller.rbに記載し適宜呼び出して使用する
application_contoller.rb
class ApplicationController < ActionController::Base

    def google_drive_initialize
        require 'google/apis/drive_v3'
        require 'googleauth'
        require 'googleauth/stores/file_token_store'
        require 'fileutils'
        credentials_dir     = '.credentials'
        @CLIENT_SECRETS_PATH = File.join(credentials_dir, 'client_secret.json')
        @CREDENTIALS_PATH    = File.join(credentials_dir, 'credentials.yaml')
        @OOB_URI             = 'urn:ietf:wg:oauth:2.0:oob'
        @APPLICATION_NAME    = 'app_name'
        @SCOPE               = Google::Apis::DriveV3::AUTH_DRIVE
        @@drive   = Google::Apis::DriveV3
        @@service = @@drive::DriveService.new
        @@service.client_options.application_name = @APPLICATION_NAME
        @@service.authorization = google_drive_authorize
    end

end

アップロード機能の実装方法

①Google Drive上に保存したいフォルダを作成してフォルダのIDを取得

②取得したIDを使ってアップロード機能を作成

form.html.slim
= form_for @image, :url=>{:controller=>"images", :action=>"google_drive_upload"} do |f|
    = f.file_field :file
    = f.submit "アップロード"
images_controller.rb
    def form
        @image = Image.new
    end

    def google_drive_upload
        # application_contoller.rbに記載した情報を読み込み
        google_drive_initialize

        # ①で取得したIDを変数に格納
        folderId = 'xxxxxx'

        # フォームから受け取ったファイルを一度railsアプリ内の「public/uploads/images/'」というフォルダに格納する
        uploadFile = images_params[:file]
        fileName = uploadFile.original_filename
        dirPath = 'public/uploads/images'
        filePath = dirPath + fileName
        Dir.chdir Rails.root
        outputPath = Rails.root.join(dirPath, fileName)
        File.open(outputPath, 'w+b') do |fp|
            fp.write uploadFile.read
        end

        # ファイルの名前やアップするフォルダの情報を記述
        file_metadata = {
            name: fileName,
            parents: [folderId]
        }

        # アップロード
        @@service.create_file(
            file_metadata,
            fields: 'id',
            upload_source: filePath,
        )

        redirect_to images_form_path
    end

    private

    def images_params
        params.require(:image).permit(:file)
    end

アップロード済みのファイルを表示する機能の実装方法

①取得したいファイル(フォルダ)があるフォルダのIDを取得

  • 取得したいファイルがあるフォルダのURLを確認し「https://drive.google.com/drive/folders/xxxxxx/」 のxxxxxxの部分が保存するフォルダのIDとなるのでコピーする

    ②取得したIDを使ってファイル表示機能を作成

    images_controller.rb
    def google_drive_file_index
        # application_contoller.rbに記載した情報を読み込み
        google_drive_initialize
    
        # ①で取得したIDを変数に格納
        folderId = 'xxxxxx'
    
        # 取得したIDをもとにGoogle Driveのフォルダにアクセスしフォルダ内の全ファイルを取得する
        @response = @@service.fetch_all(items: :files) do |page_token|
            @@service.list_files(
                q: "'#{folderId}' in parents and trashed = false",
                spaces: 'drive',
                fields: 'nextPageToken, files(id, name)',
                page_token: page_token
            )
        end
    end
    
    google_drive_file_index.html.slim
    - @response.each_with_index do |file,i|
    = file.id
    = file.name
    = link_to 'ファイルへのリンク', 'https://drive.google.com/file/d/' + file.id + '/view'
    img src="http://drive.google.com/uc?export=view&id=#{file.id}"
    - if i == 0
    p ファイルが存在しません
    - else
    p = "#{i + 1}個のファイルがあります"
    
  • eachでフォルダ内のファイルデータを一つずつ取り出す

  • @responseはファイルが存在しなくとも設定データ等を情報として持っているのでif @response.present?といった方法で存在確認をするのではなくeach_with_indexでループ回数を取得してファイルの存在確認を行う

  • file.idfile.nameでファイルが持っている情報を取り出せる

  • aタグのhrefに指定のURLを記載し、該当部分にファイルのIDを埋め込むことでファイルへのリンクを作成できる

  • imgタグのsrcにファイルのIDを埋め込むことでWebサイトに直接Google Drive内のファイルを表示させることができるが、Google Drive内のファイルアクセス権限が外部に開かれていない場合はエラーになる

アップロード済みのファイルを削除する機能の実装方法

①削除したいファイル(フォルダ)があるフォルダのIDを取得

  • 削除したいファイルがあるフォルダのURLを確認し「https://drive.google.com/drive/folders/xxxxxx/」 のxxxxxxの部分が保存するフォルダのIDとなるのでコピーする

    ②取得したIDを使ってファイル削除機能を作成

    images_controller.rb
    def google_drive_file_delete(fileId)
        # application_contoller.rbに記載した情報を読み込み
        google_drive_initialize
        @@service.delete_file(fileId)
    
        redirect_to images_google_drive_file_index_path
    end
    
    def google_drive_file_index
        # application_contoller.rbに記載した情報を読み込み
        google_drive_initialize
    
        # ①で取得したIDを変数に格納
        folderId = 'xxxxxx'
    
        # 記事内、一つ前の手順で確認したファイルを表示する機能でIDを取得する
        @response = @@service.fetch_all(items: :files) do |page_token|
            @@service.list_files(
                q: "'#{folderId}' in parents and trashed = false",
                spaces: 'drive',
                fields: 'nextPageToken, files(id, name)',
                page_token: page_token
            )
        end
    end
    
    google_drive_file_index.html.slim
    - @response.each_with_index do |file,i|
    = file.name
    = link_to '削除', images_google_drive_file_delete_path(file.id)
    
  • ループ内で取り出したファイルのIDを/images/google_drive_file_delete/にパラメーターとして送る