深度文件管理器文件拷贝原理

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

参考:Linux 中的零拷贝技术 splice

最后

具体代码实现请看:filejob.cpp

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章