c++ - registros - ejercicios de archivos en lenguaje c
Trabajador sin bloqueo: interrupciĆ³n de copia de archivo (2)
Estoy tratando con archivos muy grandes, de más de cientos de GB de tamaño. El usuario debe poder mover estos archivos entre discos y está en un sistema restringido sin un administrador de archivos predeterminado. Es posible que el Usuario se dé cuenta de que ha cometido un error y cancele la operación, y por lo que puedo decir, el Usuario tendrá que esperar a que finalice la operación actual de copia o cambio de nombre. Esto puede hacer que se sientan frustrados mientras esperan potencialmente minutos, solo para ver que sus muchos archivos GB todavía se copian. En el caso de Copiar, podría eliminar el segundo archivo, pero en el caso de cambiar el nombre, que estoy usando para mover archivos, tendría que repetir la operación a la inversa para deshacerlo, y eso simplemente no es aceptable.
¿Hay alguna forma de interrumpir copy () y renombrar () que no veo en la documentación de QFile, o tendré que armar mi propia clase para manejar copiar y renombrar?
No creo que el tamaño del archivo tenga ningún efecto sobre cuánto tiempo llevará un cambio de nombre.
Para la copia: Qt no ofrece nada integrado, debe implementarlo usted mismo. La clave aquí es que tendrá que encontrar alguna forma de sondear una cancelación de copia continuamente. Esto significa que no puede bloquear el hilo principal para poder procesar eventos.
Ya sea que elija un hilo adicional para mantener el hilo principal receptivo, o decida usar el hilo principal, en ambos casos deberá implementar una copia "fragmentada", un trozo a la vez usando un búfer, hasta que el archivo esté copiado o copiado se cancela. Necesita esto para poder procesar eventos de usuario y rastrear el progreso de la copia.
Le sugiero que implemente una clase de trabajador auxiliar de copia derivada de
QObject
que rastrea el nombre del archivo, el tamaño total, el tamaño del búfer, el progreso y la limpieza en la cancelación.
Entonces es una cuestión de elección si lo usará en el hilo principal o en un hilo dedicado.
EDITAR: Lo encontré, pero es mejor que lo verifique dos veces, ya que se hizo como un ejemplo y no se ha probado exhaustivamente:
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;
};
La señal
done()
se usa para
deleteLater()
el diálogo de ayuda de copia / copia de cierre o lo que sea.
Puede habilitar el temporizador transcurrido y usarlo para implementar una propiedad de tiempo transcurrido y también el tiempo estimado.
Pausar es otra característica posible de implementar.
El uso de
QMetaObject::invokeMethod()
permite que el bucle de eventos procese periódicamente los eventos del usuario para que pueda cancelar y actualizar el progreso, que va de 0 a 1. También puede modificarlo fácilmente para mover archivos.
No creo que exista la función que estás buscando.
Lo que puede hacer es, en lugar de usar la función copy (), crear un nuevo archivo y leer gradualmente (qint64 maxSize) a un QByteArray desde el archivo antiguo y escribir (const QByteArray & byteArray) en el nuevo archivo. De esta forma puede controlar el flujo usted mismo, solo verifique si el usuario no ha presionado cancelar entre cada lectura / escritura.