看了深度文件管理器 filejob 这个模块之后,对它的拷贝文件原理也了解了一下。
大概是这么一个流程:
- 传入两个类型为 QString 的变量:源文件地址(fromPath)、目标目录(tarDir)
- 求出目标文件完整地址:目标目录 + 源文件名,targetPath = tarDir + fromPath.fileName()
- 定义了写入数据块大小:DATA_BLOCK_SIZE 为 65536
- 创建两个 QFile 对象(源文件地址与目标文件地址)
- 调用 QFile::open() 方法,源文件打开模式为只读(ReadOnly),目标文件打开模式为只写(WriteOnly)
- 调用 QIODevice::read() 方法,每次读取一块字节数据,会返回读取的字节数(如果为0则代表拷贝成功,-1 说明失败)
- 调用 QIODevice::write() 循环写入一个数据块,也会返回写入的字节数,如果为0就会停止循环,说明文件拷贝成功
使用 Qt 读写类 QFile 来进行文件读写操作,都会调用到系统 open、read、write...
计算拷贝文件百分比进度也很简单,在写入过程中把已写入字节数存储,已写入字节数 / 总字节数 * 100.0 就是当前拷贝文件的进度,计算的公式也就是:
percent = bytesCopied / TotalByte * 100.0
拷贝文件简单实现
#include <QFile>
#include <QFileInfo>
#define DATA_BLOCK_SIZE 1000
bool copyFile(const QString &fromPath, const QString &targetDir)
{
QFileInfo fromFileInfo(fromPath);
QString targetPath = QString("%1/%2").arg(targetDir).arg(fromFileInfo.fileName());
QFile fromFile(fromPath);
QFile targetFile(targetPath);
char dataBuffer[DATA_BLOCK_SIZE];
quint64 bytesCopied = 0;
quint64 totalByte = fromFile.size();
// 如果源文件不可读直接返回 false
if (!fromFile.open(QIODevice::ReadOnly)) {
return false;
}
// 如果目标文件不可写直接返回 false
if (!targetFile.open(QIODevice::WriteOnly)) {
return false;
}
while (true) {
// 读取一段数据块
qint64 inBytes = fromFile.read(dataBuffer, DATA_BLOCK_SIZE);
if (inBytes == 0) {
fromFile.close();
targetFile.close();
// 写入完成后设置目标文件的权限
// 保持目标文件与源文件权限是一致的
targetFile.setPermissions(fromFile.permissions());
return true;
} else if (inBytes == -1) {
fromFile.close();
targetFile.close();
return false;
}
qint64 availableBytes = inBytes;
while (true) {
// 写入一段数据块
qint64 writtenBytes = targetFile.write(dataBuffer, availableBytes);
availableBytes = availableBytes - writtenBytes;
// 一段数据块写入完成后终止循环
if (writtenBytes == 0 && availableBytes == 0){
break;
}
}
// 输出百分比进度
bytesCopied += inBytes;
qDebug() << bytesCopied / totalByte * 100.0;
}
return false;
}
拷贝文件夹
拷贝文件夹通过遍历文件夹每个文件然后调用 copyFile() 进行拷贝操作。
遍历目录拷贝文件之前根据 inode 对所有文件进行一次排序,然后再进行拷贝操作。
Linux 零拷贝技术
Linux 中提供类似的系统调用主要有 sendfile()、mmap() 和splice()
最后
具体代码实现请看:filejob.cpp