FUSEを使用してYandex.Diskおよび同様のサービスのクライアントプログラムを作成する方法についてお話したいと思います。 プログラムには、シンプルだがきれいなGUIがあります。
何が必要ですか
プログラムの準備では、次の材料を使用します。
- C ++
- Qt(4.x)
- カール
- libxml
- FUSE(Windows用Dokan )
- Yandex.Disk API
すべてがパブリックドメインです。 これをすべて見つけてダウンロードするのに問題はないと思います。
計画どおり、プログラムは使いやすいはずです。 LinuxとWindowsの両方で動作するはずです。 さらに、他のサービスを接続してプログラムの機能を比較的簡単に拡張できるようにしたいと考えています。 VKontakte、Google.Docsなどのようなもの
建築
プログラムは、次の大きなブロックで構成されます。
- UI
- FUSEまたはDokan(好みに合わせて選択)
- ローカルドライバー
- リモートドライバー
- コネクター
- そして、これらすべてを接続する部分は、Commonと呼びましょう。
メインブロックとそのコンポーネントを含む図は次のようになります。
UI
ここでは何も面白いとは思いません。 通常のQt。 設定ダイアログは次のようになります。
ヒューズ
ファイルシステムドライバーを使用して、必要なファイルに対するすべての操作を追跡し、すべての変更をサードパーティのサービスに通知できます。 たとえば、写真を仮想ディスクに保存できます。 次に、たとえばGimpで開き、編集します。 次に、変更をGimpに直接保存すると、これらの変更は自動的にYandex.Diskクラウドに分類されます。 FUSEは特定の写真が変更されたことを通知し、これらの変更をクラウドに送信することができます。 実際、他の方法でファイルの変更を追跡できますが、FUSEオプションが最も興味深いように見えました。 それは非常に複雑であることを認識する価値がありますが。 たとえば、同じWindowsでDokanの呼び出しが正しく処理されないと、死のブルースクリーンが簡単に表示されます。
ローカルドライバー
これは、実際にはユーザー空間のファイルシステムドライバーのラッパーです。 私たちの場合、これらのドライバーはオペレーティングシステムに応じてFUSEまたはDokanです。 このラッパー自体のタスクは、ファイルシステムドライバーへの呼び出しに応答し、それらを共通部分に転送することです。 FUSEのバックエンドをDokanに、またはその逆に置き換えることができるようにラッパーを必要としますが、プログラムの残りの部分では何も変更しません。 Fuseでは、次のファイルシステムドライバー呼び出しを処理する必要があります。
static int fuseGetAttr(const char *path, struct stat *statbuf); static int fuseReadLink(const char *path, char *link, size_t size); static int fuseMknod(const char *path, mode_t mode, dev_t dev); static int fuseMkdir(const char *path, mode_t mode); static int fuseUnlink(const char *path); static int fuseRmdir(const char *path); static int fuseSymlink(const char *path, const char *link); static int fuseRename(const char *path, const char *newpath); static int fuseLink(const char *path, const char *newpath); static int fuseChmod(const char *path, mode_t mode); static int fuseChown(const char *path, uid_t uid, gid_t gid); static int fuseTruncate(const char *path, off_t newSize); static int fuseUtime(const char *path, struct utimbuf *ubuf); static int fuseOpen(const char *path, struct fuse_file_info *fileInfo); static int fuseRead(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fileInfo); static int fuseWrite(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fileInfo); static int fuseStatfs(const char *path, struct statvfs *statInfo); static int fuseFlush(const char *path, struct fuse_file_info *fileInfo); static int fuseRelease(const char *path, struct fuse_file_info *fileInfo); static int fuseFsync(const char *path, int datasync, struct fuse_file_info *fi); static int fuseSetxAttr(const char *path, const char *name, const char *value, size_t size, int flags); static int fuseGetxAttr(const char *path, const char *name, char *value, size_t size); static int fuseListxAttr(const char *path, char *list, size_t size); static int fuseRemovexAttr(const char *path, const char *name); static int fuseOpenDir(const char *path, struct fuse_file_info *fileInfo); static int fuseReadDir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fileInfo); static int fuseReleaseDir(const char *path, struct fuse_file_info *fileInfo); static int fuseFsyncDir(const char *path, int datasync, struct fuse_file_info *fileInfo); static void* fuseInit(struct fuse_conn_info *conn); static int fuseUtimens(const char *path, const struct timespec ts[2]);
ここでは実装を行いません。 プロジェクトリポジトリのlinux_lvfs_driver.cppファイルで確認できます
Windowsのアナログはここにあります
リモートドライバー
ここで小さな余談をしなければなりません。 上で書いたように、プログラムの要件の1つは、さまざまなサードパーティサービスを接続する機能でした。 これを行うには、Qtを使用して実装されたプラグインシステムを使用します。 プラグインについても知りたい場合は、インターネット上で多くの情報を見つけることができます。
したがって、ここでのリモートドライバーは、いくつかの抽象的なプラグインを制御するための抽象化です。 リモートドライバーを介して、プログラムの共通部分は、サードパーティサービスとの作業を実装する特定のプラグインと通信します。
コネクター
コネクタは、プログラムのもう1つの非常に重要な部分です。 コネクタのタスクは、APIを使用してさまざまなサービスの作業を抽象化することです。 Yandex.Disk API、Yandex.Photo、またはVKontakteです。 Yandex.Diskのコネクタクラスの宣言は次のとおりです。
class YaDiskHTTPConnector : public QObject { Q_OBJECT public: YaDiskHTTPConnector(); ~YaDiskHTTPConnector(); void setSettings(const QString& login , const QString& password , const QString& proxy , const QString& proxyLoginPwd , bool isOAuth , const QString& token); RESULT getTreeElements(const QString& path, QString& response); RESULT downloadFile(const QString& url, const QString& path); RESULT downloadFiles(const QList <QString>& urlList, const QList <QString>& pathList); RESULT uploadFile(const QString& path, const QString& title, const QString& parentId, QString& response); RESULT deleteFile(const QString& path, QString& response); RESULT createDirectory(const QString& title, const QString& parentId, QString& response); RESULT moveElement(const QString& id, const QString& oldParentId, const QString& newParentId, ElementType type, QString& response); RESULT renameElement(const QString& id, ElementType type, const QString& newTitle, QString& response); void setToken(const QString& token); private: static size_t writeStr(void *ptr, size_t size, size_t count, void *response); static size_t fwrite_b(void *ptr, size_t size, size_t count, void *path); static size_t readStr(void *ptr, size_t size, size_t nmemb, void *stream); static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream); int execQuery(const QString &url, const QString &header, const QString &postFields, QString* response); private: struct sPutData { const char* m_data; size_t m_len; }; private: QString m_login; QString m_password; QString m_proxy; QString m_proxyLoginPwd; bool m_isOAuth; QString m_token; QString m_requestId; QString m_key; QMutex m_connectorMutex; };
実際、サードパーティサービスへの他のコネクタも同様の外観をしています。 現在、次のサービスのコネクタが実装されています。
- Yandex.Disk
- Yandex.Photos
- Facebook(写真を使用)
- Vkontakte(写真の使用)
- Google.Docs
Yandex.Diskのコネクタ実装は、このファイルにあります 。 よく知られているCURLは、リクエストをサービスに送信するために使用されます。
まあそれはすべてです
これらすべてを組み合わせることで、シンプルなクライアントを2つのオペレーティングシステムとさまざまなクラウドサービスで動作させることができます。
プロジェクトソースの完全版はこちらにあります。
プログラムの開発を支援したい場合-ようこそ!
PS Windowsのインストーラーへのリンク。 現在、Linuxでコンパイルする方法はありません。 あとで投稿します。