+1

Thao tác với Google Drive API

Trong bài viết này mình sẽ thao tác với Google Drive API sử dụng Ruby on Rails với gem google-api-client để thực hiện một số tác vụ cơ bản như upload file, tạo thư mục mới...

1. Thiết lập môi trường

Thêm gem "google-api-client" vào Gemfile và chạy bundle install để cài gem vừa thêm vào.

2. Tạo khóa xác thực trên Google Developers Console

Truy cập vào Google Developers Console để tạo 1 project. Sau khi khởi tạo xong, click tiếp vào button Go to credentials.
Tại màn hình API Manager, chọn tab Credentials, trong New credentials, chọn OAuth client ID client.png

Nếu xuất hiện cảnh báo To create an OAuth client ID, you must first set a product name on the consent screen, bạn chuyển đến OAuth consent screen để khai báo thêm thông tin, tại đây yêu cầu chính là khai báo Product name shown to users để hiện thị khi yêu cầu người dùng cấp quyền. Sau đó có thể quay lại tab Credentials thực hiện lại.
Sau khi hoàn thành, ta sẽ được cung cấp CLIENT_IDCLIENT_SECRET. Lưu lại 2 thông số này vào biến môi trường của Rails.

3. Tạo thư viện xác thực

Tạo controller google_authenticates sử dụng để xác thực với Google.
Tạo file lib/google/auth.rb với nội dung như sau

module Google
  SCOPES = [
    "https://www.googleapis.com/auth/drive",
    "email",
    "profile"
  ]
  REDIRECT_URI = "http://localhost:3000/google_authenticates"
  CLIENT_ID = ENV["CLIENT_ID"]
  CLIENT_SECRET = ENV["CLIENT_SECRET"]

  class Auth
    class << self
      def get_authorization_url
        client = Google::APIClient.new
        client.authorization.client_id = CLIENT_ID
        client.authorization.redirect_uri = REDIRECT_URI
        client.authorization.scope = SCOPES

        return client.authorization.authorization_uri(
          approval_prompt: :force,
          access_type: :offline
        ).to_s
      end

      def exchange_code authorization_code
        client = Google::APIClient.new
        client.authorization.client_id = CLIENT_ID
        client.authorization.client_secret = CLIENT_SECRET
        client.authorization.code = authorization_code
        client.authorization.redirect_uri = REDIRECT_URI

        begin
          client.authorization.fetch_access_token!
          return client.authorization
        rescue Signet::AuthorizationError
          raise CodeExchangeError.new nil
        end
      end

      def build_client credentials
        client = Google::APIClient.new
        client.authorization = credentials
        client
      end

      def build_drive client
        client.discovered_api("drive", "v2")
      end

      def build_oauth
        client.discovered_api("oauth2", "v2")
      end
    end
  end

  class GetCredentialsError < StandardError
    def initialize authorization_url
      @authorization_url = authorization_url
    end

    def authorization_url=(authorization_url)
      @authorization_url = authorization_url
    end

    def authorization_url
      @authorization_url
    end
  end

  class CodeExchangeError < GetCredentialsError
  end

  class NoRefreshTokenError < GetCredentialsError
  end
end
  • get_authorization_url sẽ tạo ra đường link xác thực với require ta mong muốn dựa trên SCOPE ta khai báo. access_type: :offline sẽ cho phép ta lấy access token mới khi access token cũ hết hạn mà không cần yêu cầu người dùng xác nhận lại.
  • exchange_code sẽ trả lại credentials dựa trên authorization_code lấy được sau khi xác nhận dựa trên link do get_authorization_url sinh ra. Việc sử dụng gem google-api-client giúp chúng ta không cần phải thao tác trực tiếp với token hay REST, tất cả tác vụ cơ bản gần như đã được gói gọn lại để sử dụng.

4. Tạo client để thao tác với Google Drive

Ta tạo file lib/google/client.rb với nội dung :

module Google
  FOLDER_MIME = "application/vnd.google-apps.folder"

  class Client
    attr_reader :client, :drive

    def initialize credentials
      @client = build_client credentials
      @drive = build_drive client
    end

    def get_user_info
      oauth2= client.discovered_api "oauth2", "v2"
      result = client.execute! api_method: oauth2.userinfo.get
      if result.status == 200
        result
      else
        false
      end
    end

    def get_files
      client.execute api_method: drive.files.list
    end

    def get_file file_id
      client.execute api_method: drive.files.get, parameters: {fileId: file_id}
    end

    def create_folder folder_name
      folder = drive.files.insert.request_schema.new({
        title: folder_name,
        mimeType: FOLDER_MIME
      })

      client.execute api_method: drive.files.insert, body_object: folder
    end

    def insert_file file_path, title: nil, parent_id: nil, description: nil
      mime_type = MIME::Types.type_for(file_path).first.content_type
      file = drive.files.insert.request_schema.new({
        title: title || File.basename(file_path),
        description: description,
        mimeType: mime_type
      })
      if parent_id
        file.parents = [{id: parent_id}]
      end
      media = Google::APIClient::UploadIO.new file_path, mime_type
      result = client.execute api_method: drive.files.insert,
        body_object: file,
        media: media,
        parameters: {
          uploadType: "multipart",
          alt: "json"
        }
    end

    private
    def build_client credentials
      client = Google::APIClient.new
      client.authorization = credentials
      client
    end

    def build_drive client
      client.discovered_api("drive", "v2")
    end
  end
end

Ta có thể thấy việc thực hiện các tác vụ với Google Drive cơ bản là gọi các hàm do Google tạo sẵn như :

  • `client.execute api_method: drive.files.list để lấy danh sách các file trong drive.
  • client.execute api_method: drive.files.get, parameters: {fileId: file_id} để get file trên drive dựa trên id của nó.
  • client.execute api_method: drive.files.insert ... để upload file lên drive.

Trước khi đưa file vào trong params thực hiện việc upload file, ta cần gói lại file đó theo chuẩn của Google (Mình nghĩ đây chính là quá trình mã hóa file theo chuẩn Base64). drive.files.insert.request_schema.new.
Đặc biệt Google Drive API không hỗ trợ tạo folder trực tiếp mà coi folder đó như 1 file đặc biệt với MIME là application/vnd.google-apps.folder, vậy nên để tạo 1 folder mới, ta đơn giản chỉ là sử dụng api để tạo file mới có MIME là application/vnd.google-apps.folder.

5. Kết luận

2 đoạn code trên đã có thể giúp ta thực hiện các tác vụ cơ bản với Google Drive cũng như minh họa được luồng hoạt động khi sử dụng API để tương tác. Một điểm mình khá thích là khi thực hiện client.execute, nếu token hiện tại hết hạn, nó sẽ tự get token mới về dựa trên các credentials truyền vào khi khởi tạo client. Việc lưu trữ credentials để sử dụng ở các phiên làm việc khác khá đơn giản bằng viện convert nó thành JSON và lưu vào Database, chi tiết việc này sẽ để bạn đọc tự thực hiện.
Bài viết trên đã mô tả các bước thực hiện, có code mẫu cũng như giải thích những hàm chính. Do kiến thức còn hạn chế nên sẽ có những sai sót, mong bạn đọc góp ý để mình hoàn thiện kiến thức cũng như nội dung bài viết.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí