C++ 非阻塞工作进程-中断文件复制

C++ 非阻塞工作进程-中断文件复制,c++,qt,qt4.8,qfile,C++,Qt,Qt4.8,Qfile,我处理的文件非常大,超过数百GB。用户需要能够在磁盘之间移动这些文件,并且位于没有默认文件管理器的受限系统上。用户可能会意识到他们犯了错误并取消操作,据我所知,用户将不得不等待当前的复制或重命名操作完成。这会让他们感到沮丧,因为他们可能会等上几分钟,结果却发现他们的多GB文件仍然被复制。在复制的情况下,我可以删除第二个文件,但是在重命名的情况下,我用它来移动文件,我必须反向重复这个操作来撤销它,这是不可接受的 有没有什么方法可以中断我在QFile文档中没有看到的copy()和rename(),或

我处理的文件非常大,超过数百GB。用户需要能够在磁盘之间移动这些文件,并且位于没有默认文件管理器的受限系统上。用户可能会意识到他们犯了错误并取消操作,据我所知,用户将不得不等待当前的复制或重命名操作完成。这会让他们感到沮丧,因为他们可能会等上几分钟,结果却发现他们的多GB文件仍然被复制。在复制的情况下,我可以删除第二个文件,但是在重命名的情况下,我用它来移动文件,我必须反向重复这个操作来撤销它,这是不可接受的


有没有什么方法可以中断我在QFile文档中没有看到的copy()和rename(),或者我需要创建自己的类来处理copy和rename?

我认为您正在寻找的函数不存在

您可以做的是,不使用copy()函数,而是创建一个新文件,并从旧文件中逐步读取(qint64 maxSize)QByteArray,然后将(const QByteArray&byteArray)写入新文件。
通过这种方式,您可以自己控制流量,只需检查用户在每次读/写之间是否没有按“取消”。

我认为文件大小对重命名所需的时间没有任何影响

因为copy-Qt没有内置任何东西,所以您必须自己实现它。这里的关键问题是,你必须找到一种方法来不断地投票取消拷贝。这意味着您无法锁定主线程以处理事件

无论您是为了让主线程保持响应而选择额外的线程,还是决定使用主线程—在这两种情况下,您都需要实现“分段”复制—使用缓冲区一次复制一个块,直到文件被复制或复制被取消。您需要这样才能处理用户事件并跟踪复制进度

我建议您实现一个
QObject
派生的copy helper worker类,它跟踪文件名、总大小、缓冲区大小、进度和取消时的清理。然后,您可以选择是在主线程中使用它,还是在专用线程中使用它

编辑:找到了,但您最好仔细检查一下,因为它是作为一个示例完成的,尚未经过彻底测试:

class CopyHelper : public QObject {
    Q_OBJECT
    Q_PROPERTY(qreal progress READ progress WRITE setProgress NOTIFY progressChanged)
public:
    CopyHelper(QString sPath, QString dPath, quint64 bSize = 1024 * 1024) :
        isCancelled(false), bufferSize(bSize), prog(0.0), source(sPath), destination(dPath), position(0) { }
    ~CopyHelper() { free(buff); }

    qreal progress() const { return prog; }
    void setProgress(qreal p) {
        if (p != prog) {
            prog = p;
            emit progressChanged();
        }
    }

public slots:
    void begin() {
        if (!source.open(QIODevice::ReadOnly)) {
            qDebug() << "could not open source, aborting";
            emit done();
            return;
        }
        fileSize = source.size();
        if (!destination.open(QIODevice::WriteOnly)) {
            qDebug() << "could not open destination, aborting";
            // maybe check for overwriting and ask to proceed
            emit done();
            return;
        }
        if (!destination.resize(fileSize)) {
            qDebug() << "could not resize, aborting";
            emit done();
            return;
        }
        buff = (char*)malloc(bufferSize);
        if (!buff) {
            qDebug() << "could not allocate buffer, aborting";
            emit done();
            return;
        }
        QMetaObject::invokeMethod(this, "step", Qt::QueuedConnection);
        //timer.start();
    }
    void step() {
        if (!isCancelled) {
            if (position < fileSize) {
                quint64 chunk = fileSize - position;
                quint64 l = chunk > bufferSize ? bufferSize : chunk;
                source.read(buff, l);
                destination.write(buff, l);
                position += l;
                source.seek(position);
                destination.seek(position);
                setProgress((qreal)position / fileSize);
                //std::this_thread::sleep_for(std::chrono::milliseconds(100)); // for testing
                QMetaObject::invokeMethod(this, "step", Qt::QueuedConnection);
            } else {
                //qDebug() << timer.elapsed();
                emit done();
                return;
            }
        } else {
            if (!destination.remove()) qDebug() << "delete failed";
            emit done();
        }
    }
    void cancel() { isCancelled = true; }

signals:
    void progressChanged();
    void done();

private:
    bool isCancelled;
    quint64 bufferSize;
    qreal prog;
    QFile source, destination;
    quint64 fileSize, position;
    char * buff;
    //QElapsedTimer timer;
};
类CopyHelper:公共QObject{
Q_对象
Q_属性(qreal progress READ progress WRITE setProgress NOTIFY progressChanged)
公众:
CopyHelper(QString sPath、QString dPath、quint64 bSize=1024*1024):
isCancelled(false)、bufferSize(bSize)、prog(0.0)、源(sPath)、目标(dPath)、位置(0){
~CopyHelper(){free(buff);}
qreal progress()常量{return prog;}
无效设置进度(QRP){
如果(p!=prog){
prog=p;
发生了变化();
}
}
公众时段:
void begin(){
如果(!source.open(QIODevice::ReadOnly)){

qDebug()我很害怕。我宁愿花几个小时在其他地方。我会用你建议的模式做一些事情,它必须在另一个线程中,以确保GUI可以随进度更新。好的一面是,我可以通过这种方式给出特定于文件的进度。至于rename(),我用它来移动文件。当你移动到同一磁盘上的另一个目录时,它是不变的,但从一个磁盘到另一个磁盘确实需要时间,这取决于文件大小。@MildWolfie-我想我已经做过类似的事情了,几小时后回到家时会检查。这就是我为了保持UI响应而做的事情nsive.@MildWolfie-找到它,可能会为您节省一些时间。