2015-10-05 2 views
3

Я имею дело с очень большими файлами размером более сотни ГБ. Пользователь должен иметь возможность перемещать эти файлы между дисками и находится в ограниченной системе без файлового менеджера по умолчанию. Пользователь может понять, что они допустили ошибку и отменили операцию, и насколько я могу судить, пользователю придется дождаться завершения текущей операции копирования или переименования. Это может вызвать у них чувство разочарования, поскольку они ждут потенциально минут, только чтобы увидеть, что их много файлов GB все еще копируются. В случае с копией я могу удалить второй файл, но в случае переименования, который я использую для перемещения файлов, мне пришлось бы повторить операцию в обратном порядке, чтобы отменить ее, и это просто неприемлемо.Неблокирующий работник - копия файла прерывания

Есть ли способ прервать copy() и rename(), который я не вижу в документации для QFile, или мне нужно собрать собственный класс для обработки копии и переименования?

ответ

6

Я не думаю, что размер файла влияет на продолжительность переименования.

Для копии - Qt не предлагает ничего встроенного, вы должны реализовать его самостоятельно. Ключевым вопросом здесь является то, что вам нужно будет найти способ опроса для отмены копирования непрерывно. Это означает, что вы не можете заблокировать основной поток, чтобы иметь возможность обрабатывать события.

Если вы идете на дополнительный поток, чтобы не реагировать на основной поток или не решили использовать основной поток - в обоих случаях вам понадобится реализовать «фрагментированное» копирование - по одному фрагменту за один раз с использованием буфера, пока файл не будет скопирован или копирование отменено. Это нужно, чтобы обрабатывать пользовательские события и отслеживать ход копирования.

Я предлагаю вам создать класс рабочего помощника-помощника QObject, который отслеживает имя файла, общий размер, размер буфера, прогресс и очистку от отмены. Тогда будет вопросом, будете ли вы использовать его в основном потоке или в выделенном потоке.

EDIT: Нашел, но лучше дважды проверить это, так как это было сделано в качестве примера и не были тщательно протестированы:

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; 
}; 

Сигнал done() используется для deleteLater() копия хелперов/закрыть копию диалог или что угодно. Вы можете включить истекший таймер и использовать его для реализации свойства истекшего времени и расчетного времени. Приостановка - еще одна возможная функция для реализации. Использование QMetaObject::invokeMethod() позволяет циклу событий периодически обрабатывать пользовательские события, чтобы вы могли отменить и обновить ход, который идет от 0 до 1. Вы можете легко настроить его для перемещения файлов.

+0

Я боялся этого. Просто случай часов, который я бы предпочел провести в другом месте. Я сделаю что-то с шаблоном, который вы предложили, и он должен быть в другом потоке, чтобы гарантировать, что графический интерфейс может обновиться с прогрессом. С яркой стороны я смогу дать конкретный прогресс файла таким образом. – MildWolfie

+0

Что касается rename(), я использую его для перемещения файлов. Когда вы переходите в другой каталог на одном диске, он постоянно, но переход с одного диска на другой занимает много времени в зависимости от размера файла. – MildWolfie

+0

@MildWolfie - Я думаю, что уже сделал что-то подобное, проверит, когда вернусь домой через пару часов. – dtech

1

Я не думаю, что функция, которую вы ищете, существует.

Что вы можете сделать, это вместо того, чтобы использовать функцию копирования(), создайте новый файл и gradualy чтения (qint64 MaxSize) к QByteArray из старого файла и записи (константный QByteArray & ByteArray,) в новый файл. Таким образом вы можете контролировать поток самостоятельно, просто проверьте, не нажимал ли пользователь отмену между каждым чтением/записью.

Смежные вопросы