效果
核心代码
由于 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/tree/CopyFile
参考: