Qt 中實現非同步雜湊器

梦起丶發表於2024-09-11

【寫在前面】

在很多工作中,我們需要計算資料或者檔案的雜湊值,例如登入或下載檔案。

而在 Qt 中,負責這項工作的類為 QCryptographicHash

關於 QCryptographicHash

QCryptographicHash 是 Qt 框架中提供的一個用於生成加密雜湊(雜湊值)的類。該類可以將任意長度的輸入(二進位制或文字資料)轉換成固定長度的輸出(雜湊值),這一過程是不可逆的。QCryptographicHash 支援多種雜湊演算法,包括 MD4、MD5、SHA-1、SHA-224、SHA-256、SHA-384 和 SHA-512 等,這些演算法在資料完整性校驗、密碼儲存、數字簽名等應用場景中非常有用。

主要特點:

  1. 支援多種雜湊演算法:QCryptographicHash 提供了多種雜湊演算法的支援,允許開發者根據具體需求選擇合適的演算法。
  2. 簡單易用的介面:QCryptographicHash 提供了簡單易用的介面來計算雜湊值。開發者可以透過呼叫 QCryptographicHash::hash() 靜態方法或建立 QCryptographicHash 物件並使用 addData()result() 方法來計算雜湊值。
  3. 逐塊計算:`QCryptographicHash 還可以逐塊地計算雜湊值,這對於處理大檔案或流式資料非常有用。
  4. 可重複使用:QCryptographicHash 物件可以多次使用。當計算完一個雜湊值後,可以透過呼叫 reset() 方法重置物件,然後繼續計算新的雜湊值。

然鵝, 雖然 QCryptographicHash 很優秀,但它最大的問題在於其雜湊值的計算是同步的( 即阻塞 ),對小資料來說並沒什麼影響,但對大資料來說則意味明顯示卡頓。

因此,我將 QCryptographicHash 進行簡單封裝,擴充套件了實用性的同時並將計算改為非同步,還增加了進度通知和結束通知。


【正文開始】

先來看看 AsyncHasher 的使用效果圖:

image

AsyncHasher 的使用方法非常簡單:

  1. 包含標頭檔案:在使用 AsyncHasher 之前,需要包含相應的標頭檔案 #include "asynchasher.h"

  2. 透過 setSource / setSourceText / setSourceData/ setSourceObject 設定源目標。

  3. 透過 void hashProgress(qint64 processed, qint64 total) 來獲取進度,void finished() 通知計算結束。

  4. 透過 QString hashValue() const 獲取最終結果。

例如 C++ 使用:

    AsyncHasher *hasher = new AsyncHasher;
    hasher->setSourceText("Test Text");
    QObject::connect(hasher, &AsyncHasher::finished, [hasher]{
        qDebug() << hasher->hashValue();
    });

並且我還做了 Qml 適配,使用方法:

    AsyncHasher {
        id: textHasher
        algorithm: AsyncHasher.Md5
        onStarted: {
            startTime = Date.now();
        }
        onFinished: {
            totalTime = Date.now() - startTime;
            console.log("HashValue:", hashValue, "time:", totalTime);
        }
        property real startTime: 0
        property real totalTime: 0
    }

完整標頭檔案如下:

#ifndef ASYNCHASHER_H
#define ASYNCHASHER_H

#include <QCryptographicHash>
#include <QFuture>
#include <QObject>
#include <QUrl>

QT_FORWARD_DECLARE_CLASS(QNetworkAccessManager);

QT_FORWARD_DECLARE_CLASS(AsyncHasherPrivate);

class AsyncHasher : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QCryptographicHash::Algorithm algorithm READ algorithm WRITE setAlgorithm NOTIFY algorithmChanged)
    Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged)
    Q_PROPERTY(QString hashValue READ hashValue NOTIFY hashValueChanged)
    Q_PROPERTY(int hashLength READ hashLength NOTIFY hashLengthChanged)
    Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
    Q_PROPERTY(QString sourceText READ sourceText WRITE setSourceText NOTIFY sourceTextChanged)
    Q_PROPERTY(QByteArray sourceData READ sourceData WRITE setSourceData NOTIFY sourceDataChanged)
    Q_PROPERTY(QObject* sourceObject READ sourceObject WRITE setSourceObject NOTIFY sourceObjectChanged)

public:
    Q_ENUMS(QCryptographicHash::Algorithm);

    explicit AsyncHasher(QObject *parent = nullptr);
    ~AsyncHasher();

    QNetworkAccessManager *networkManager() const;

    QCryptographicHash::Algorithm algorithm();
    void setAlgorithm(QCryptographicHash::Algorithm algorithm);

    bool asynchronous() const;
    void setAsynchronous(bool async);

    QString hashValue() const;

    int hashLength() const;

    QUrl source() const;
    void setSource(const QUrl &source);

    QString sourceText() const;
    void setSourceText(const QString &sourceText);

    QByteArray sourceData() const;
    void setSourceData(const QByteArray &sourceData);

    QObject *sourceObject() const;
    void setSourceObject(QObject *sourceObject);

    bool operator==(const AsyncHasher &hasher);
    bool operator!=(const AsyncHasher &hasher);

    QFuture<QByteArray> static hash(const QByteArray &data, QCryptographicHash::Algorithm algorithm);

signals:
    void algorithmChanged();
    void asynchronousChanged();
    void hashValueChanged();
    void hashLengthChanged();
    void sourceChanged();
    void sourceTextChanged();
    void sourceDataChanged();
    void sourceObjectChanged();
    void hashProgress(qint64 processed, qint64 total);
    void started();
    void finished();

private slots:
    void setHashValue(const QString &value);

private:
    Q_DECLARE_PRIVATE(AsyncHasher);
    QScopedPointer<AsyncHasherPrivate> d_ptr;
};

#endif // ASYNCHASHER_H

【結語】

最後:專案連結(多多star呀..⭐_⭐):

Github 地址:https://github.com/mengps/QmlControls/tree/master/AsyncHasher

相關文章