一、需求分析:
随着渲染业务的不断进行,数据传输渐渐成为影响业务时间最大的因素。究其原因就是因为数据传输耗费较长的时间。于是,依托于渲染业务的网盘开发逐渐成为迫切需要解决的需求。该网盘的实现和当前市场上网盘实现有一些的不同,主要在客户端与服务器端的操作需要双向进行,即:用户在客户端的操作需要及时同步到服务器端;在服务器端作业渲染生成的文件要及时同步到客户端。即:用户不在需要单独的下载数据,而是在作业运行的同时,渲染就过就会自动同步到客户端,大大缩短了等待时间。当然,无论是在客户端还是在服务端都面临着一个问题,即:实现对文件操作的监控,这里的文件操作包括:文件(夹)创建、文件(夹)删除、文件(夹)重命名、文件(夹)移动等操作。除此之外还要能够同步客户端文件的修改操作,即:当用户退出网盘后,修改了原有同步目录中的文件,当用户再次启动网盘时通过一次扫描与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的窗口句柄。关键代码如下:
- void houqd::RegisterWindow()
- {
- char absoluteFolderPath[] = "C:\\openssl";
- //! 由文件夹的绝对路径获取PIDL:外壳对象标识符列表,即在windows 外壳名字空间 "Shell Name Space"中的表示方法。
- LPITEMIDLIST myFolderPIDL = ParsePidlFromPath(absoluteFolderPath);
- HRESULT res ;
- IShellItem *psi = NULL;
- //! 创建一个IShellItem(interface)对象,IShellItem interface提供了查找一个关于Shell Item相关信息的方法。
- //! IShell Item接口都继承自IUnknown interface
- res = SHCreateShellItem(NULL, NULL, myFolderPIDL, &psi);
- IShellItem2 *ppsi ;
- //! 检索一个对象上支持的接口的指针
- psi->QueryInterface(&ppsi);
- //! =======================================注册文件监控=============================================
- PIDLIST_ABSOLUTE pidlWatch;
- HRESULT hr = SHGetIDListFromObject(ppsi, &pidlWatch);
- if(SUCCEEDED(hr))
- {
- SHChangeNotifyEntry const entries[] = { pidlWatch, true };
- int const nSources = SHCNRF_ShellLevel | SHCNRF_InterruptLevel | SHCNRF_NewDelivery;
- //! 注册窗口主函数
- _ulRegister = SHChangeNotifyRegister(_hdlg, nSources, SHCNE_ALLEVENTS, c_notifyMessage, ARRAYSIZE(entries), entries);
- hr = _ulRegister != 0 ? S_OK : E_FAIL;
- }
- //ShowWindow(SW_HIDE);
- //! ====================================================================================
- }
ParsePidFromPath 的具体实现如下:
- LPITEMIDLIST houqd::ParsePidlFromPath(LPCSTR lpszPath)
- {
- //存放以Unicode内码表示的路径字符串的缓冲区
- OLECHAR szOleChar[MAX_PATH];
- //“桌面“的IshellFolder接口指针
- LPSHELLFOLDER lpsfDeskTop;
- //返回的PIDL
- LPITEMIDLIST lpifq;
- ULONG ulEaten, ulAttribs;
- HRESULT hres;
- //得到“桌面”的IshellFolderr 接口指针
- SHGetDesktopFolder(&lpsfDeskTop);
- //将Ansi字符集的路径字符串转换成Unicode字符串,存入szOleChar
- MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED, lpszPath, -1, szOleChar, sizeof(szOleChar));
- //将szOleChar,中的路径径字符串翻译成相应的PIDL,存入lpifq
- hres = lpsfDeskTop->ParseDisplayName(NULL, NULL, szOleChar, &ulEaten, &lpifq, &ulAttribs);
- hres = lpsfDeskTop->Release();
- //如果翻译失败,则返回NULL
- if(FAILED(hres))
- return NULL;
- return lpifq;
- }
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);
关键代码如下:
- if (m_InotifyFd != LHFSMC_FD_UNCREATED_STATE)
- close(m_InotifyFd);
- //! inotify_init()
- if ((m_InotifyFd = inotify_init()) < 0)
- {
- qDebug() << "[error] LHFileSystemMonitor::Start: inotify_init failure.";
- return 0;
- }
- //! 为m_CreatedDirList中所有保存的目录创建监控
- if(!CreateWatcherForEachDir(m_CreatedDirList))
- {
- qDebug() << "[error] LHFileSystemMonitor::Start: CreateNotifierForEachDir failure.";
- return 0;
- }
CreateWatcherForEachDir的实现:
- int LHFileSystemMonitor::CreateWatcherForEachDir(QStringList &dirLocationList)
- {
- #ifndef WIN32
- for (QStringList::const_iterator iter = dirLocationList.begin();
- iter != dirLocationList.end();
- ++iter)
- {
- int watchDescriptor;
- if ((watchDescriptor = CreateWatcher(iter->toStdString().c_str())) > 0)
- m_MonitoredObjectList.push_back(LHFSMonitorData(*iter, watchDescriptor));
- else
- qDebug() << "[error] LHFileSystemMonitor::CreateWatcherForEachDir: CreateWatcher for"
- << *iter << "failure";
- }
- #endif
- return 1;
- }
CreateWatcher的实现:
- int LHFileSystemMonitor::CreateWatcher(const char *fileLocation) const
- {
- if (fileLocation == NULL)
- return 0;
- QDir dir(fileLocation);
- if (!dir.exists())
- {
- qDebug() << "[error] LHFileSystemMonitor::CreateNotifier:" << fileLocation << "is not exist!";
- return 0;
- }
- return ((m_InotifyFd != LHFSMC_FD_UNCREATED_STATE) ? inotify_add_watch(m_InotifyFd, fileLocation, LHFSMC_MONITOR_EVENT) : -1);
- }
总结:
以上就是Windows以及Linux下文件监控系统实现的相关思路及代码,仅仅作为一个引入,利用这种方式均可以实现对应的功能。
Windows与Linux下文件操作监控的实现的更多相关文章
-
Windows、Linux下文件操作(写、删除)错误的产生原因、及解决方法
catalog . 引言 . Linux平台上涉及的File IO操作 . Windows平台上涉及的File IO操作 0. 引言 本文试图讨论在windows.linux操作系统上基于C库进行文件 ...
-
linux下文件操作之cp和mv
Linux CP文件夹略过目录的解决 root@QGY:/home/qgy# cp image/newimage_raw /mnt/4T/qin/cp: 略过目录'image/newimage_raw ...
-
Linux 下文件操作 shell
删除目录下的所有文件ls *.log | xargs rm -f当前目录所有文件大小的总和ll | awk '{sum += $5}; END {print sum/1048576}'将命令推送到后台 ...
-
Linux下 文件操作(base)
1.新建文件夹 mkdir bigdata:在当前文件夹下新建bigdata文件夹: 2.显示当前文件夹全目录 pwd: 3.移动文件:mv /usr/etc/spark-2.3.1-bin-hado ...
-
windows与linux之间文件的传输方式总结(转)
当然,windows与linux之间文件的传输的两种方式有很多,这里就仅仅列出工作中遇到的,作为笔记: 方法一:安装SSH Secure Shell Client客户端 安装即可登录直接拖拉到linu ...
-
linux下文件结束符
linux下文件结束符,我试过了所有的linux,发现其文件的结束符都是以0a即LF结束的,这个是操作系统规定的,windows下是\r\n符结束,希望可以帮助大家. -------------转:来 ...
-
Windows 和 Linux 下 禁止ping的方法
Windows 和Linux 下 禁止ping的方法 目的: 禁止网络上的其他主机或服务器ping自己的服务器 运行环境: Windows 03.08 linux 方法: Windows 03下: ...
-
socket在windows下和linux下的区别
原文:socket在windows下和linux下的区别 1)头文件 windows下winsock.h/winsock2.h linux下sys/socket.h 错误处理:errno.h 2 ...
-
Linux下的IO监控与分析
Linux下的IO监控与分析 近期要在公司内部做个Linux IO方面的培训, 整理下手头的资料给大家分享下 各种IO监视工具在Linux IO 体系结构中的位置 源自 Linux Performan ...
随机推荐
-
Prim算法(三)之 Java详解
前面分别通过C和C++实现了普里姆,本文介绍普里姆的Java实现. 目录 1. 普里姆算法介绍 2. 普里姆算法图解 3. 普里姆算法的代码说明 4. 普里姆算法的源码 转载请注明出处:http:// ...
-
DIV+CSS+JS基础+正则表达式
...............HTML系列.................... DIV元素是用来为HTML文档内大块(block-level)的内容提供结构和背景的元素.DIV的起始 ...
-
Spark Shuffle实现
Apache Spark探秘:Spark Shuffle实现 http://dongxicheng.org/framework-on-yarn/apache-spark-shuffle-details ...
-
水题~~~~HDU 4788
Description Yesterday your dear cousin Coach Pang gave you a new 100MB hard disk drive (HDD) as a gi ...
-
java转c#代码工具集合
1#:Java语言转换器助手地址:http://www.microsoft.com/en-us/download/details.aspx?id=14349 2#:Octopus的.NET转换器地址: ...
-
PHP 类中的常量
类中的常量与静态成员类似他们只属于类而不属于类的任何实例,访问形式与访问静态成员一样. 例如: <?php class MyConst{ const RED="Red"; c ...
-
【原创】leetCodeOj --- Find Minimum in Rotated Sorted Array II 解题报告
题目地址: https://oj.leetcode.com/problems/find-minimum-in-rotated-sorted-array-ii/ 题目内容: Suppose a sort ...
-
LBS基站定位
LBS基站定位(Location Based Service,简称LBS)一般应用于手机用户,它是基于位置的服务,通过电信.移动运营商的无线电通讯网络(如GSM网.CDMA网)或外部定位方式(如GPS ...
-
sench touch 自定义小图标(转)
自定义图标的方法 Sencha touch自带图标有限,有时需要自己添加图标.下面介绍自定义图标的方法: 首先需要生成图标字体.有许多网站提供在线生成图标字体的功能,比如IcoMoon,通过这个网站, ...
-
原生JS获取DOM 节点到浏览器顶部的距离或者左侧的距离
关于js获取dom 节点到浏览器顶/左部的距离,Jquery里面有封装好的offset().top/offset().left,只到父级的顶/左部距离position().top/position() ...