【Qt Demo】异步拷贝文件(可取消)

效果


核心代码

由于 Qt 的中 QFile::copy 是个原子操作,所以并不支持拷贝文件进度。所以用 QThread 实现了在线程中拷贝文件,并能实时更新文件进度,主要代码封装在 FileCopyer 类里。(可以跨平台)

FileCopyer.h

#pragma once

#include <QtCore/qstring.h>
#include <QtCore/qobject.h>
#include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qvector.h>
#include <QtCore/qthread.h>

class FileCopyer : public QObject {
    Q_OBJECT
        Q_PROPERTY(qint64 chunksize READ chunkSize WRITE setChunkSize)
        Q_PROPERTY(QVector<QString> sourcePaths READ sourcePaths WRITE setSourcePaths)
        Q_PROPERTY(QVector<QString> destinationPaths READ destinationPaths WRITE setDestinationPaths)

public:
    static const int DEFAULT_CHUNK_SIZE = 1024 * 1024 * 1;

    FileCopyer(QThread*);
    ~FileCopyer();

    qint64 chunkSize() const {
        return _chunk;
    }
    void setChunkSize(qint64 ch) {
        _chunk = ch;
    }

    QVector<QString> sourcePaths() const {
        return src;
    }
    void setSourcePaths(const QVector<QString>& _src) {
        src = _src;
    }

    QVector<QString> destinationPaths() const {
        return dst;
    }
    void setDestinationPaths(const QVector<QString>& _dst) {
        dst = _dst;
    }

    void interrupt()
    {
        _interrupt = true;
    }

    protected slots:
    void copy();



private:
    QVector<QString> src, dst;
    qint64 _chunk;
    bool _interrupt;

signals:
    void copyProgress(qint64 bytesCopied, qint64 bytesTotal);
    void finished(bool success);
    void oneBegin(QString srcFileName);
    void oneFinished(QString dstPath, bool result);
};

FileCopyer.cpp

#include <QtCore/qdebug.h>
#include "FileCopyer.h"

FileCopyer::FileCopyer(QThread* _thread) :QObject(nullptr),
    _interrupt(false){
    moveToThread(_thread);
    setChunkSize(DEFAULT_CHUNK_SIZE);

    QObject::connect(_thread, &QThread::started, this, &FileCopyer::copy);
    QObject::connect(this, &FileCopyer::finished, _thread, &QThread::quit);
    //QObject::connect(this, &FileCopyer::finished, this, &FileCopyer::deleteLater);
    QObject::connect(_thread, &QThread::finished, _thread, &QThread::deleteLater);
}

FileCopyer::~FileCopyer() {

}

void FileCopyer::copy()
{
    if (src.isEmpty() || dst.isEmpty())
    {
        qWarning() << QStringLiteral("source or destination paths are empty!");
        emit finished(false);
        return;
    }

    if (src.count() != dst.count())
    {
        qWarning() << QStringLiteral("source or destination paths doesn't match!");
        emit finished(false);
        return;
    }

    qint64 total = 0, written = 0;
    for (const auto& f : src)
        total += QFileInfo(f).size();
    qInfo() << QStringLiteral("%1 bytes should be write in total").arg(total);

    int indx = 0;
    qInfo() << QStringLiteral("writing with chunk size of %1 byte").arg(chunkSize());
    while (indx < src.count())
    {
        const auto dstPath = dst.at(indx);

        QFile srcFile(src.at(indx));
        QFile dstFile(dstPath);
        if (QFile::exists(dstPath))
        {
            qInfo() << QStringLiteral("file %1 alreasy exists, overwriting...").arg(dstPath);
            QFile::remove(dstPath);
        }

        if (!srcFile.open(QFileDevice::ReadOnly))
        {
            qWarning() << QStringLiteral("failed to open %1 (error:%2)").arg(srcFile.fileName()).arg(srcFile.errorString());
            indx++;
            continue; // skip
        }

        if (!dstFile.open(QFileDevice::WriteOnly))
        {
            qWarning() << QStringLiteral("failed to open %1 (error:%2)").arg(dstFile.fileName()).arg(dstFile.errorString());
            indx++;
            continue; // skip
        }
        emit oneBegin(QFileInfo(srcFile).fileName());
        /* copy the content in portion of chunk size */
        qint64 fSize = srcFile.size();
        while (fSize) {
            if(_interrupt)
            {
                emit finished(true);
                return;
            }
            const auto data = srcFile.read(chunkSize());
            const auto _written = dstFile.write(data);
            if (data.size() == _written)
            {
                written += _written;
                fSize -= data.size();
                emit copyProgress(written, total);
            }
            else
            {
                emit oneFinished(dstPath, false);
                qWarning() << QStringLiteral("failed to write to %1 (error:%2)").arg(dstFile.fileName()).arg(dstFile.errorString());
                fSize = 0;
                break; // skip this operation
            }
        }

        srcFile.close();
        dstFile.close();
        emit oneFinished(dstPath, true);
        indx++;
        qDebug() << QThread::currentThreadId();
    }

    if (total == written)
    {
        qInfo() << QStringLiteral("progress finished, %1 bytes of %2 has been written").arg(written).arg(total);
        emit finished(true);
    }
    else
    {
        emit finished(false);
    }
}

调用如下:

auto local = new QThread;
auto worker = new FileCopyer(local);
QObject::connect(worker, &FileCopyer::finished, [](bool s) {
	s ? qDebug() << "FINISHED" : qDebug() << "FAILED";
});
QObject::connect(worker, &FileCopyer::copyProgress, [](qint64 copy, qint64 total) {
	qDebug() << QStringLiteral("PROGRESS => %1").arg(qreal(copy) / qreal(total) * 100.0);
});
worker->setSourcePaths(/* src-paths */); // e.g: ~/content/example.mp4
worker->setDestinationPaths(/* dst-paths */); // e.g /usr/local/example.mp4
local->start();

GitHub下载地址

下载地址为:https://github.com/confidentFeng/QtAppProject.git


参考:

基于Qt的异步拷贝文件

qt 拷贝文件设置进度条


posted @ 2020-08-10 16:12  fengMisaka  阅读(969)  评论(0编辑  收藏  举报