Skip to content
This repository has been archived by the owner on Oct 3, 2022. It is now read-only.
/ GDriveClientApp Public archive

A Demo for Qt OAuth2 & Qt socket execute Google Drive API

License

Notifications You must be signed in to change notification settings

Loukei/GDriveClientApp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GDriveClientApp project(Archived)

tags: C 11 Qt5 OAuth 2.0

簡介 Introduction

目前Google Drive Api並沒有官方支援的C Lib,雖然網路上也有一些不錯的專案比如googleQt或是o2等...。

一方面我們不能滿足於都用別人寫好的工具,另一方面我也想透過這個專案學習親手操作網路API。

透過Qt OAuth2模組,利用Socket操作google drive api對本地上傳或下載檔案。

螢幕截圖 Screenshot

功能 Features

  • OAuth2 帳號登入(須開啟瀏覽器)

  • 取得使用者名稱與email

  • 上傳檔案

  • 下載檔案,需要輸入fileID

  • 搜尋,取得檔案fileID

使用應用程式

  1. 要開發Google OAuth 2 App,您應該向google申請自己的一組App,並在Google Api Console下載Client_secret.json
  2. 下載並執行GDrive.exe
  • 初次使用無法登入Google,因為您還沒有配置設定檔
  • 關閉程式,會看到GDriveApp_Settings.ini
  • 開啟GDriveApp_Settings.ini,編輯OAuth欄位底下的內容,依照您自己的API設定填入前三項ClientId、ClientSecert、RedirectUri
[OAuth]
ClientId=CLIENTID
ClientSecert=CLIENTSECRET
RedirectUri=http://localhost:8080/cb
Scope=https://www.googleapis.com/auth/drive.file
AuthUri=https://accounts.google.com/o/oauth2/auth
TokenUri=https://oauth2.googleapis.com/token
  • 重新開啟GDrive.exe並登入即可

如何使用GDriveLib How to Use library

範例 Example

  1. 在專案內加入必要的模組QT = core gui network networkauth
  2. src/GDriveLib資料夾加入專案
#include "GDriveLib/googledriveservice.h"

ExampleDialog::ExampleDialog(QWidget *parent)
    : QDialog(parent),m_currentOAuthToken(QString())
{
    m_Drive = new GDriveService(Settings::OAuth_AuthUri(m_settings),
                                Settings::OAuth_TokenUri(m_settings),
                                Settings::OAuth_ClientId(m_settings),
                                Settings::OAuth_ClientSecert(m_settings),
                                Settings::OAuth_Scope(m_settings),
                                Settings::OAuth_RedirectPort(m_settings),
                                this);
    connect(m_Drive,&GDriveService::granted,
            this,&ExampleDialog::onGDrive_granted);
    connect(m_Drive,&GDriveService::error,
            this,&ExampleDialog::onGDrive_error);
    connect(m_Drive,&GDriveService::tokenChanged,
            this,&ExampleDialog::onGDrive_tokenChanged);
    // setup UI...
}
// login user
void ExampleDialog::login()
{
    m_Drive->grant();
}
// logout
void ExampleDialog::logout()
{
    m_Drive->setToken("");
    m_Drive->setRefreshToken("");
}
// simple upload local file
void ExampleDialog::uploadFile()
{
    const QString filepath = "D:/Download/testdata/soviet example.txt";
    auto task = m_Drive->fileSimpleCreate(filepath);
    if(!task->start()){
        /* For upload/download/update task, use start() to check the relate file has open or not. */
        m_textbrowser->append(filepath   " Simple Upload error:"   task->errorString());
        task->deleteLater();
        return;
    }
    auto onUploadreceive = [task,this,filepath](){
        if(task->isComplete() && !task->isFailed()){
            m_textbrowser->append(filepath   " Simple Upload Success.\n");
        }else {
            m_textbrowser->append(filepath   " Simple Upload error:"   task->errorString());
        }
        task->deleteLater();
    };
    connect(task,&GDriveFileTask::finished,
            this,onUploadreceive);
}
void ExampleDialog::onGDrive_granted()
{
    qDebug() << "Token: " << m_Drive->token()
            << "Refresh token:" << m_Drive->refreshToken();
}
void ExampleDialog::onGDrive_error(const QString &error, const QString &errorDescription, const QUrl &uri)
{
    /* handle error here */
    QVariantMap info;
    info.insert(QStringLiteral("Error"),error);
    info.insert(QStringLiteral("Description"),errorDescription);
    info.insert(QStringLiteral("Uri"),uri);
    qDebug() << info;
}
void ExampleDialog::onGDrive_tokenChanged(const QString &token)
{
    /* m_currentOAuthToken = {}, token = "..." => login
     * m_currentOAuthToken = "a...", token = "b..." => refresh token or switch account
     * m_currentOAuthToken = "...", token = {} => logout */
    if(token.isEmpty()){
        qDebug() << "token isEmpty -> logout";
    }else if (m_currentOAuthToken.isEmpty()) {
        qDebug() << "m_currentOAuthToken isEmpty() && token !isEmpty() -> login";
    }else if (token != m_currentOAuthToken) {
        qDebug() << "token != m_currentOAuthToken -> refresh or switch account";
    }
    m_currentOAuthToken = token; /*change current token*/
}

說明

設定OAuth api

建立一個GDriveService物件需要以下幾個參數:

QUrl authorizationUrl
QUrl accessTokenUrl
QString clientIdentifier
QString clientIdentifierSharedKey
QString scope
quint16 port

可以從你的google api設定裡填入,或是使用oauthglobal.h幫助

登入

void GDriveService::grant();

登出

// 清除Access token與Refresh token
GDriveService::setToken("");
GDriveService::setRefreshToken("");

上傳檔案

GDriveFileSimpleCreate *GDriveService::fileSimpleCreate(const QString &filepath)

API使用任務導向操作,物件內部會發出http socket、處理上傳流程與回傳結果

auto task = m_Drive->fileSimpleCreate(filepath);

針對檔案上傳/下載/更新的動作需要開啟本地端檔案,為了確保在網路請求發出前做出確認,這類Task使用bool start()進行檢查,如果傳入的檔案路徑開啟成功就會送出請求,反之會失敗並回傳false

if(!task->start()){
    // 印出失敗訊息...
    qDebug() << "file open fail";
    task->deleteLater();
    return;
}

連接finished()信號,準備接收回傳結果,當任務完成或失敗都會產生信號

connect(task,&GDriveFileTask::finished,this,onUploadreceive);

準備一個函式負責處理task的結果

isComplete()表示任務是否完成

isFailed()表示任務是否失敗

使用QByteArray getReplyString()接收回傳內容

當任務完成後,使用QObject::deleteLater()刪除資料,不要使用delete

auto onUploadreceive = [task,this,filepath](){
    if(task->isComplete() && !task->isFailed()){
        qDebug() << filepath   " Simple Upload Success.";
    }else {
        qDebug() << filepath   " Simple Upload error:"   task->errorString();
    }
    task->deleteLater();
};

額外: 如果需要顯示上傳/下載進度,參考QNetworkReply::uploadProgress

void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);

以範例中的情況就是使用 GDriveFileSimpleCreate::uploadProgress

信號處理

  • 當認證流程成功或者access token更新,GDriveService::granted()會發出,用來確認可以執行OAuth操作。

  • 發生認證問題時使用GDriveService::error(const QString &error, const QString &errorDescription, const QUrl &uri)信號處裡。

  • 利用GDriveService::tokenChanged(const QString &token)處理access token的變化。

  • GDriveService相關的信號說明可以參照QOAuth2AuthorizationCodeFlow

偵測登入/登出/更新Token

OAuth 2並沒有設定登出的機制,實際上要取消登入只要刪除本地端的Refresh token與access token即可。

開發者不會希望在沒有access token的情況下呼叫網路作業,這會導致程式錯誤甚至崩潰。

可以使用GDriveService::tokenChanged()以及紀錄前一個token的內容來開啟/關閉網路操作。

當token改變時,通過與前一個token之間的比較我們可以了解目前的狀態

  • token = {}, 則為logout,關閉相關的網路操作,並開啟login選項。
  • token != {} 且 current_token != {},代表token更新
  • token != {} 且 current_token = {},代表現在已登入,關閉login選項

不要使用GDriveService::granted()判定是否登入,因為更新access token時也會觸發此信號

保存使用者帳號,簡化登入操作

為了方便APP使用者無須重複登入google取得授權,你可以選擇保存refresh token在本地端並加密內容,下一次程式開啟時載入refresh token,並呼叫GDriveService::refreshAccessToken()取得access token,完成登入操作。

m_Drive->setRefreshToken(refreshToken);
m_Drive->refreshAccessToken(); // use refresh token to login

編譯環境 My compile environment

  • C ver: C 11
  • Qt kits: Qt5.13.2 MinGW 64bit
  • OS: win10 64bit

Credit

參考 Reference

  1. Connecting your Qt application with Google Services using OAuth 2.0

  2. Download files

  3. Upload files

  4. Files: list

  5. Search for files and folders

About

A Demo for Qt OAuth2 & Qt socket execute Google Drive API

Resources

License

Stars

Watchers

Forks

Packages

No packages published