「Qtの応用 - rsync」の版間の差分

ナビゲーションに移動 検索に移動
135行目: 135行目:


== ローカルディレクトリとの同期 ==
== ローカルディレクトリとの同期 ==
以下の例では、librsyncライブラリを使用して、ファイルおよびディレクトリを非同期でファイルを差分転送している。<br>
以下の例では、librsyncライブラリを使用して、同一PC上のファイルおよびディレクトリを非同期でファイルを差分転送している。<br>
<br>
<br>
* 非同期処理
* 非同期処理
299行目: 299行目:
   
   
     return a.exec();
     return a.exec();
}
</syntaxhighlight>
<br><br>
== リモートPCのディレクトリとの同期 ==
以下の例では、librsyncライブラリを使用して、リモートPC上のファイルおよびディレクトリを非同期でファイルを差分転送している。<br>
<br>
* 非同期処理
*: QtConcurrent::runメソッドを実行して、ファイル同期を非同期で実行する。
*: QFutureクラスを使用して、非同期処理の結果を管理する。
* ネットワーク通信
*: QTcpSocketクラスを使用して、リモートホストと通信する。
* ストリーミング処理
*: ファイルデータとコマンドをストリーミングで送受信する。
* ディレクトリ構造の維持
*: ソースディレクトリの構造を宛先ディレクトリに複製する。
<br>
<syntaxhighlight lang="c++">
// RemoteFileSyncWorker.hファイル
#include <QObject>
#include <QDir>
#include <QDirIterator>
#include <QFileInfo>
#include <QDataStream>
#include <QFuture>
#include <QtConcurrent>
#include <QTcpSocket>
#include <librsync.h>
class RemoteFileSyncWorker : public QObject
{
    Q_OBJECT
private:
    QTcpSocket *socket;
    void syncFile(const QString &sourcePath, const QString &destPath)
    {
      QFile sourceFile(sourcePath);
      if (!sourceFile.open(QIODevice::ReadOnly)) {
          emit errorOccurred(QString("ソースファイルのオープンに失敗: %1").arg(sourcePath));
          return;
      }
      // リモートファイルのシグネチャを要求
      QByteArray command = "SIGNATURE:" + destPath.toUtf8();
      if (!sendData(command)) {
          emit errorOccurred("シグネチャ要求の送信に失敗");
          return;
      }
      QByteArray remoteSignature = receiveData();
      if (remoteSignature.isEmpty()) {
          emit errorOccurred("リモートシグネチャの受信に失敗");
          return;
      }
      // デルタの計算
      rs_result result;
      rs_buffers_t buf;
      char inbuf[8192];
      char outbuf[8192];
      rs_job_t *job = rs_delta_begin(new rs_signature_t());
      if (!job) {
          emit errorOccurred("デルタジョブの作成に失敗");
          return;
      }
      do {
          buf.next_in  = inbuf;
          buf.avail_in  = sourceFile.read(inbuf, sizeof(inbuf));
          buf.next_out  = outbuf;
          buf.avail_out = sizeof(outbuf);
          result = rs_job_iter(job, &buf);
          if (buf.avail_out < sizeof(outbuf)) {
            QByteArray deltaData(outbuf, sizeof(outbuf) - buf.avail_out);
            if (!sendData(deltaData)) {
                emit errorOccurred("デルタデータの送信に失敗");
                rs_job_free(job);
                return;
            }
          }
      } while (result == RS_BLOCKED);
      rs_job_free(job);
      if (result != RS_DONE) {
          emit errorOccurred(QString("デルタの計算に失敗しました: %1").arg(rs_strerror(result)));
          return;
      }
      // 完了通知
      if (!sendData("DONE")) {
          emit errorOccurred("完了通知の送信に失敗");
          return;
      }
      emit progressUpdated(1);
    }
    bool connectToHost(const QString &host, quint16 port)
    {
      socket->connectToHost(host, port);
      return socket->waitForConnected(5000);
    }
    void disconnectFromHost()
    {
      if (socket->state() == QAbstractSocket::ConnectedState) {
          socket->disconnectFromHost();
      }
    }
    bool sendData(const QByteArray &data)
    {
      QDataStream out(socket);
      out << quint32(data.size());
      out.writeRawData(data.constData(), data.size());
      return socket->waitForBytesWritten(5000);
  }
    QByteArray receiveData()
    {
      if (!socket->waitForReadyRead(5000)) {
          return QByteArray();
      }
      QDataStream in(socket);
      quint32 blockSize;
      in >> blockSize;
      while (socket->bytesAvailable() < blockSize) {
          if (!socket->waitForReadyRead(5000)) {
            return QByteArray();
          }
      }
      QByteArray data;
      data.resize(blockSize);
      in.readRawData(data.data(), blockSize);
      return data;
    }
public:
    explicit RemoteFileSyncWorker(QObject *parent = nullptr) : QObject(parent), socket(new QTcpSocket(this))
    {}
    ~RemoteFileSyncWorker()
    {
      disconnectFromHost();
    }
    QFuture<void> syncFiles(const QString &sourcePath, const QString &destPath, const QString &host, quint16 port)
    {
      return QtConcurrent::run([this, sourcePath, destPath, host, port]() {
          if (!connectToHost(host, port)) {
            emit errorOccurred("リモートホストへの接続に失敗しました");
            return;
          }
          QDirIterator it(sourcePath, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
          while (it.hasNext()) {
            QString sourceFilePath = it.next();
            QString relativeFilePath = QDir(sourcePath).relativeFilePath(sourceFilePath);
            QString destFilePath = QDir(destPath).filePath(relativeFilePath);
            QFileInfo sourceInfo(sourceFilePath);
            if (sourceInfo.isDir()) {
                // リモートPCでディレクトリを作成するコマンドを送信
                QByteArray command = "MKDIR:" + destFilePath.toUtf8();
                if (!sendData(command)) {
                  emit errorOccurred("ディレクトリ作成コマンドの送信に失敗");
                  return;
                }
            }
            else {
                syncFile(sourceFilePath, destFilePath);
            }
          }
          disconnectFromHost();
      });
    }
signals:
    void progressUpdated(int progress);
    void errorOccurred(const QString &error);
};
#endif // REMOTEFILESYNCWORKER_H
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
#include <QCoreApplication>
#include <QDebug>
#include "RemoteFileSyncWorker.h"
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QString sourcePath = "<同期元ディレクトリ>";
    QString destPath  = "<同期先ディレクトリ>";
    QString remoteHost = "<リモートホストのIPアドレス>";
    quint16 remotePort = <リモートホストのポート番号>;
    RemoteFileSyncWorker worker;
    QObject::connect(&worker, &RemoteFileSyncWorker::progressUpdated, [](int progress) {
      qDebug() << "進捗: " << progress;
    });
    QObject::connect(&worker, &RemoteFileSyncWorker::errorOccurred, [](const QString &error) {
      qDebug() << "エラー: " << error;
    });
    // 同期の開始
    QFuture<void> future = worker.syncFiles(sourcePath, destPath, remoteHost, remotePort);
    // メインイベントループを開始
    int result = a.exec();
    // 同期完了まで待機
    future.waitForFinished();
    return result;
  }
  }
  </syntaxhighlight>
  </syntaxhighlight>

案内メニュー