Неблокирующий рабочий - прерывание копирования файла
Я имею дело с очень большими файлами, размер которых превышает сотни ГБ. Пользователь должен иметь возможность перемещать эти файлы между дисками и находится в ограниченной системе без файлового менеджера по умолчанию. Пользователь может понять, что он совершил ошибку, и отменить операцию, и, насколько я могу судить, пользователю придется ждать завершения текущей операции копирования или переименования. Это может вызвать у них чувство разочарования, поскольку они потенциально могут ждать минуты, только чтобы увидеть, что их многобайтовый файл все еще скопирован. В случае копирования я мог удалить второй файл, но в случае переименования, которое я использую для перемещения файлов, мне пришлось бы повторить операцию в обратном порядке, чтобы отменить его, и это просто неприемлемо.
Есть ли какой-нибудь способ прервать copy() и rename(), которого я не вижу в документации по QFile, или мне нужно будет собрать свой собственный класс для обработки копирования и переименования?
2 ответа
Я не думаю, что размер файла влияет на то, сколько времени займет переименование.
Для копии - Qt не предлагает ничего встроенного, вы должны реализовать это самостоятельно. Ключевым моментом здесь является то, что вам придется постоянно искать способ отмены копии. Это означает, что вы не можете заблокировать основной поток, чтобы иметь возможность обрабатывать события.
Идете ли вы за дополнительным потоком, чтобы сохранить основной поток отзывчивым, или решаете использовать основной поток - в обоих случаях вам потребуется реализовать "фрагментированное" копирование - по одному фрагменту за раз с использованием буфера, пока файл не будет Скопировано или копирование отменено. Это необходимо для обработки пользовательских событий и отслеживания процесса копирования.
Я предлагаю вам реализовать QObject
вспомогательный рабочий класс производного копирования, который отслеживает имя файла, общий размер, размер буфера, ход выполнения и очищает при отмене. Тогда вы можете выбрать, будете ли вы использовать его в основном потоке или в отдельном потоке.
РЕДАКТИРОВАТЬ: нашел, но вам лучше проверить это дважды, так как это было сделано в качестве примера и не было тщательно проверено:
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. Вы также можете легко настроить его для перемещения файлов.
Я не думаю, что функция, которую вы ищете, существует.
Вместо использования функции copy() вы можете создать новый файл и постепенно читать (qint64 maxSize) QByteArray из старого файла и записывать (const QByteArray & byteArray) в новый файл. Таким образом, вы можете управлять потоком самостоятельно, просто проверьте, не нажимал ли пользователь отмену между каждым чтением / записью.