看了深度文件管理器 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