一、需求分析:

随着渲染业务的不断进行,数据传输渐渐成为影响业务时间最大的因素。究其原因就是因为数据传输耗费较长的时间。于是,依托于渲染业务的网盘开发逐渐成为迫切需要解决的需求。该网盘的实现和当前市场上网盘实现有一些的不同,主要在客户端与服务器端的操作需要双向进行,即:用户在客户端的操作需要及时同步到服务器端;在服务器端作业渲染生成的文件要及时同步到客户端。即:用户不在需要单独的下载数据,而是在作业运行的同时,渲染就过就会自动同步到客户端,大大缩短了等待时间。当然,无论是在客户端还是在服务端都面临着一个问题,即:实现对文件操作的监控,这里的文件操作包括:文件(夹)创建、文件(夹)删除、文件(夹)重命名、文件(夹)移动等操作。除此之外还要能够同步客户端文件的修改操作,即:当用户退出网盘后,修改了原有同步目录中的文件,当用户再次启动网盘时通过一次扫描与md5值的比较能缺确定出哪些文件发生了改动,并将改动后的操作及时同步到服务端。这里,先将Windows(客户端实现需要)与Linux(服务端实现需要)下文件监控的实现方法简要概述。

二、文件监控实现方法分析

1> Windows下文件监控的实现

Windows下实现文件监控的原理是利用SHChangeNotifyRegister把指定的窗口添加到系统的消息监视链中,从而注册窗口就可以接收到来自文件系统或Shell的通知了。在继续向下说明之前,需要解释一下Windows外壳名字空间(Shell Name Space)的概念。

外壳名字空间是Windows下的标准文件系统,它大大扩展了Dos文件系统,形成了以”桌面“为根的单一文件系统树,原有的C、D盘等目录树变成了“我的电脑”这一外壳名字控件子树的下一级子树,而像“控制面板”、“回收站”、“网上邻居”等应用程序以及“打印机”等设备也被虚拟成了外壳名字空间中的节点。为了区别于Dos中“目录”的概念,Windows引入了“文件夹”的概念。“文件夹”一般是指外壳名字空间树中的非叶几点,既可以是DOS下的目录,也可以是“控制面板”、“回收站”这类虚拟的目录。

新的“路径”PIDL:外壳对象标识符列表。PIDL是一个元素类型为ITEMIDLIST结构的数组,数组中元素的个数是未知的,但紧接着数组的末尾的必是一个双字节的零。每个数组元素代表了外壳名字空间树中的一层(即一个文件夹或文件),数组中的前一元素代表的是后一元素的父文件夹。由此可见,PIDL实际上就是指向一块由若干个顺序排列的ITEMIDLIST结构组成、并在最后有一个双字节零的空间的指针。所以PIDL的类型就被Windows定义为ITEMIDLIST结构的指针。

DOS中的路径是一个字符串,但PIDL是一种二进制结构,所以我们不能直接从PIDL中获知它所代表的到底是哪个文件夹或文件,而必须调用相应的函数把它转换为代表路径的字符串。如果某绝对PIDL是文件系统的一部分,则调用SHGetPathFromIDList函数即可;但如不是,就无法获得路径字符串了,因为DOS中根本就不存在这种路径。

总体来说:实现Windows下文件监控的基本流程如下:

上图是在windows下利用C++实现文件监控的一种方式(根据需要灵活改动),这里简要叙述一下:1) 利用DialogBoxParam创建一个模态对话框,进入窗口过程函数,在该窗口函数中根据各种消息来完成我们的操作。这里模态对话框与非模态对话框的区别之一是因为它有一套自己的消息泵机制,不需要我们再手动写消息的接收了(非模态对话框要自己接收消息)。拦截用户消息,根据各个不同的阶段可以加入我们自己的操作,比如初始化等。2) 获取指定路径的PIDL:即目标路径的外壳对象标识符,有了它才能继续后续的处理,这里的获取有两种方式,一种是利用IFileDialog打开对话框让用户选择(IFileDialog *pfd),从而通过GetResult(IShellItem *psi ; pfs->GetResult(&psi))获取IShellItem对象;然后利用QueryInterface(IShellItem2 *_psiDrop ; psi->QueryInterface(&_psiDrop))获取IShellItem2对象;最后利用它就可以获得PIDL了(利用SHGetIDListFromObject)。3) 最后利用SHChangeNotifyRegister完成最终目标窗体的挂载,从而将一个目录加入到系统的消息链中,从而可以获取文件系统或Shell中关于文件操作的相关信息。最后将信息解析出来就可以了。

还有一种方式即直接提供目标文件夹的绝对路径,由该路径获取到PIDL,从而将窗体挂载到系统消息链中,注意:如果是在QT中实现的话,可以很轻松的获取到QWidget的窗口句柄。关键代码如下:

  1. void houqd::RegisterWindow()
  2. {
  3. char absoluteFolderPath[] = "C:\\openssl";
  4. //! 由文件夹的绝对路径获取PIDL:外壳对象标识符列表,即在windows 外壳名字空间 "Shell Name Space"中的表示方法。
  5. LPITEMIDLIST myFolderPIDL = ParsePidlFromPath(absoluteFolderPath);
  6. HRESULT res ;
  7. IShellItem *psi = NULL;
  8. //! 创建一个IShellItem(interface)对象,IShellItem interface提供了查找一个关于Shell Item相关信息的方法。
  9. //! IShell Item接口都继承自IUnknown interface
  10. res = SHCreateShellItem(NULL, NULL, myFolderPIDL, &psi);
  11. IShellItem2 *ppsi ;
  12. //! 检索一个对象上支持的接口的指针
  13. psi->QueryInterface(&ppsi);
  14. //! =======================================注册文件监控=============================================
  15. PIDLIST_ABSOLUTE pidlWatch;
  16. HRESULT hr = SHGetIDListFromObject(ppsi, &pidlWatch);
  17. if(SUCCEEDED(hr))
  18. {
  19. SHChangeNotifyEntry const entries[] = { pidlWatch, true };
  20. int const nSources = SHCNRF_ShellLevel | SHCNRF_InterruptLevel | SHCNRF_NewDelivery;
  21. //! 注册窗口主函数
  22. _ulRegister = SHChangeNotifyRegister(_hdlg, nSources, SHCNE_ALLEVENTS, c_notifyMessage, ARRAYSIZE(entries), entries);
  23. hr = _ulRegister != 0 ? S_OK : E_FAIL;
  24. }
  25. //ShowWindow(SW_HIDE);
  26. //! ====================================================================================
  27. }

ParsePidFromPath 的具体实现如下:

  1. LPITEMIDLIST houqd::ParsePidlFromPath(LPCSTR lpszPath)
  2. {
  3. //存放以Unicode内码表示的路径字符串的缓冲区
  4. OLECHAR szOleChar[MAX_PATH];
  5. //“桌面“的IshellFolder接口指针
  6. LPSHELLFOLDER lpsfDeskTop;
  7. //返回的PIDL
  8. LPITEMIDLIST lpifq;
  9. ULONG ulEaten, ulAttribs;
  10. HRESULT hres;
  11. //得到“桌面”的IshellFolderr 接口指针
  12. SHGetDesktopFolder(&lpsfDeskTop);
  13. //将Ansi字符集的路径字符串转换成Unicode字符串,存入szOleChar
  14. MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED, lpszPath, -1, szOleChar, sizeof(szOleChar));
  15. //将szOleChar,中的路径径字符串翻译成相应的PIDL,存入lpifq
  16. hres = lpsfDeskTop->ParseDisplayName(NULL, NULL, szOleChar, &ulEaten, &lpifq, &ulAttribs);
  17. hres = lpsfDeskTop->Release();
  18. //如果翻译失败,则返回NULL
  19. if(FAILED(hres))
  20. return NULL;
  21. return lpifq;
  22. }

2> Linux下文件监控的实现

Linux下主要是通过inotify实现文件监控。它是一个内核用户通知用户空间程序文件系统变化的机制。在用户状,inotify通过三个系统调用和在返回的文件描述符上的文件I/O操作来使用.

1) 使用inotify的第一步是创建inotify的实例:int fd = inotify_init() ; 每一个inotify实例对应一个独立的排序的队列。

2) int wd = inotify_add_watch(fd , file_dir_path , mask);添加一个目录的监控。

3) 删除一个监控:inotify_rm_watch(fd , wd);

关键代码如下:

  1. if (m_InotifyFd != LHFSMC_FD_UNCREATED_STATE)
  2. close(m_InotifyFd);
  3. //! inotify_init()
  4. if ((m_InotifyFd = inotify_init()) < 0)
  5. {
  6. qDebug() << "[error] LHFileSystemMonitor::Start: inotify_init failure.";
  7. return 0;
  8. }
  9. //! 为m_CreatedDirList中所有保存的目录创建监控
  10. if(!CreateWatcherForEachDir(m_CreatedDirList))
  11. {
  12. qDebug() << "[error] LHFileSystemMonitor::Start: CreateNotifierForEachDir failure.";
  13. return 0;
  14. }

CreateWatcherForEachDir的实现:

  1. int LHFileSystemMonitor::CreateWatcherForEachDir(QStringList &dirLocationList)
  2. {
  3. #ifndef WIN32
  4. for (QStringList::const_iterator iter = dirLocationList.begin();
  5. iter != dirLocationList.end();
  6. ++iter)
  7. {
  8. int watchDescriptor;
  9. if ((watchDescriptor = CreateWatcher(iter->toStdString().c_str())) > 0)
  10. m_MonitoredObjectList.push_back(LHFSMonitorData(*iter, watchDescriptor));
  11. else
  12. qDebug() << "[error] LHFileSystemMonitor::CreateWatcherForEachDir: CreateWatcher for"
  13. << *iter << "failure";
  14. }
  15. #endif
  16. return 1;
  17. }

CreateWatcher的实现:

  1. int LHFileSystemMonitor::CreateWatcher(const char *fileLocation) const
  2. {
  3. if (fileLocation == NULL)
  4. return 0;
  5. QDir dir(fileLocation);
  6. if (!dir.exists())
  7. {
  8. qDebug() << "[error] LHFileSystemMonitor::CreateNotifier:" << fileLocation << "is not exist!";
  9. return 0;
  10. }
  11. return ((m_InotifyFd != LHFSMC_FD_UNCREATED_STATE) ? inotify_add_watch(m_InotifyFd, fileLocation, LHFSMC_MONITOR_EVENT) : -1);
  12. }

总结:

以上就是Windows以及Linux下文件监控系统实现的相关思路及代码,仅仅作为一个引入,利用这种方式均可以实现对应的功能。

Windows与Linux下文件操作监控的实现的更多相关文章

  1. Windows、Linux下文件操作(写、删除)错误的产生原因、及解决方法

    catalog . 引言 . Linux平台上涉及的File IO操作 . Windows平台上涉及的File IO操作 0. 引言 本文试图讨论在windows.linux操作系统上基于C库进行文件 ...

  2. linux下文件操作之cp和mv

    Linux CP文件夹略过目录的解决 root@QGY:/home/qgy# cp image/newimage_raw /mnt/4T/qin/cp: 略过目录'image/newimage_raw ...

  3. Linux 下文件操作 shell

    删除目录下的所有文件ls *.log | xargs rm -f当前目录所有文件大小的总和ll | awk '{sum += $5}; END {print sum/1048576}'将命令推送到后台 ...

  4. Linux下 文件操作(base)

    1.新建文件夹 mkdir bigdata:在当前文件夹下新建bigdata文件夹: 2.显示当前文件夹全目录 pwd: 3.移动文件:mv /usr/etc/spark-2.3.1-bin-hado ...

  5. windows与linux之间文件的传输方式总结(转)

    当然,windows与linux之间文件的传输的两种方式有很多,这里就仅仅列出工作中遇到的,作为笔记: 方法一:安装SSH Secure Shell Client客户端 安装即可登录直接拖拉到linu ...

  6. linux下文件结束符

    linux下文件结束符,我试过了所有的linux,发现其文件的结束符都是以0a即LF结束的,这个是操作系统规定的,windows下是\r\n符结束,希望可以帮助大家. -------------转:来 ...

  7. Windows 和  Linux 下 禁止ping的方法

    Windows 和Linux 下 禁止ping的方法 目的: 禁止网络上的其他主机或服务器ping自己的服务器 运行环境: Windows 03.08  linux 方法: Windows 03下: ...

  8. socket在windows下和linux下的区别

    原文:socket在windows下和linux下的区别 1)头文件 windows下winsock.h/winsock2.h linux下sys/socket.h    错误处理:errno.h 2 ...

  9. Linux下的IO监控与分析

    Linux下的IO监控与分析 近期要在公司内部做个Linux IO方面的培训, 整理下手头的资料给大家分享下 各种IO监视工具在Linux IO 体系结构中的位置 源自 Linux Performan ...

随机推荐

  1. nutch http file 截断问题

    问题: 列表页预计抽取 355+6 但实际只抽取到220条链接. 原因是nutch对http下载的内容的长度进行了限制. 解决方案:这里将这个属性扩大10倍. vim conf/nutch-defal ...

  2. Hbase案例分析(二)

    情景1:

  3. 哈希,哈希表,哈希Map

    数组: 数组存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二分查找时间复杂度小,为O(1):数组的特点是:寻址容易,插入和删除困难: 链表: 链表存储区间离散,占用内存比较宽松,故空间复杂 ...

  4. bzoj 2482: [Spoj GSS2] Can you answer these queries II 线段树

    2482: [Spoj1557] Can you answer these queries II Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 145 ...

  5. csu 10月 月赛 F 题 ZZY and his little friends

    一道字典树异或的题,但是数据比较水,被大家用暴力给干掉了! 以前写过一个类似的题,叫做the longest xor in tree: 两个差不多吧! 好久没写字典树了,复习一下! 代码: #incl ...

  6. 字符编码:ANSI,ASCII,GB2312,GBK,Big5,Unicode和UTF-8

    整理自字符编码笔记:ASCII,Unicode和UTF-8 1. ASCII码 我们知道,在计算机内部,所有的信息最终都表示为一个二进制的字符串.每一个二进制位(bit)有0和1两种状态,因此八个二进 ...

  7. 【HDU 5456】 Matches Puzzle Game (数位DP)

    Matches Puzzle Game Problem Description As an exciting puzzle game for kids and girlfriends, the Mat ...

  8. ZOJ3582:Back to the Past(概率DP)

    Recently poet Mr. po encountered a serious problem, rumor said some of his early poems are written b ...

  9. USACO3.34Home on the Range(DP)

    之前做过一道类似的 国际象棋盘神马的.. 统计出以每个1作为右下角的最大正方形 那么以大于二到这个最大值之间为边的正方形都可以以这个为右下角 累加就可以了 dp[i][j] = min(dp[i-1] ...

  10. bzoj3530

    比较恶心的题目不难发现是在自动机上做数位dp注意要考虑前导0,题目中给出的233是幸运数,20233不是为此我非常猥琐的写了一个四维dp,用记忆化搜索实现 ; ..,..,..,..] of long ...