watchFile.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /**
  2. * 1.监听文件变动
  3. */
  4. const fs = require("fs");
  5. const path = require("path");
  6. const tools = require("./tools");
  7. /**
  8. * 事件回调
  9. *
  10. * @callback WatchEventCallback
  11. * @param {string} eventName - 事件名 create | delete | change | init
  12. * @param {Array<string>} files - 改变的文件
  13. */
  14. /**
  15. * 监听事件
  16. */
  17. class WatchFile {
  18. /**
  19. * @param {WatchFile} watchObj
  20. * @param {string} pathName
  21. * @param {WatchEventCallback} eventCallback
  22. */
  23. constructor(watchObj,pathName,eventCallback){
  24. this.pathName = pathName;
  25. this.eventCallback = eventCallback;
  26. this._watchObj = watchObj;
  27. /** @type Object<string,fs.Stats> */
  28. this._files = {}
  29. this._isChecking = false;
  30. this._isInit = false;
  31. this._isExistPath = false;
  32. this.check();
  33. }
  34. stop(){
  35. this._watchObj.removeWatch(this.eventCallback);
  36. }
  37. /**
  38. * 更新文件状态
  39. */
  40. async check(){
  41. if(this._isChecking){
  42. return;
  43. }
  44. let pathStat = fs.existsSync(this.pathName) ? fs.statSync(this.pathName) : undefined;
  45. if(!pathStat){
  46. // 监听的目录被删除
  47. if(this._isExistPath) setTimeout(this._removeAllFile.bind(this),1);
  48. this._isExistPath = false;
  49. return ;
  50. }
  51. this._isExistPath = true;
  52. this._isChecking = true;
  53. let newFiles = {}
  54. if(pathStat && pathStat.isDirectory())
  55. {
  56. newFiles = await getDirAllFiles(this.pathName,newFiles)
  57. this._update(newFiles)
  58. }else if(pathStat.isFile())
  59. {
  60. newFiles[this.pathName] = pathStat;
  61. // 延迟执行防止循环调用
  62. setTimeout(()=>this._update(newFiles),1)
  63. }
  64. }
  65. /**
  66. * 更新状态
  67. * @param {Object<string,fs.Stats>} newFiles
  68. */
  69. _update(newFiles){
  70. let deleteFiles = [];
  71. let changeFiles = [];
  72. let createFiles = [];
  73. for (const filePath in this._files) {
  74. const oldFileStats = this._files[filePath];
  75. const newFileStats = newFiles[filePath]
  76. if(newFileStats == null){
  77. // 文件被删
  78. deleteFiles.push(filePath);
  79. }else if(newFileStats.mtimeMs != oldFileStats.mtimeMs){
  80. // 文件被修改
  81. changeFiles.push(filePath);
  82. }
  83. }
  84. for (const filePath in newFiles) {
  85. /** @type fs.Stats */
  86. const oldFileStats = this._files[filePath];
  87. if(oldFileStats == null){
  88. // 新创文件
  89. createFiles.push(filePath);
  90. }
  91. }
  92. let isInit = this._isInit;
  93. this._files = newFiles;
  94. this._isChecking = false;
  95. this._isInit = true;
  96. // 调用事件
  97. if(this.eventCallback){
  98. if(!isInit){
  99. this.eventCallback('init',createFiles);
  100. }else{
  101. if(deleteFiles.length) this.eventCallback('delete',deleteFiles);
  102. if(changeFiles.length) this.eventCallback('change',changeFiles);
  103. if(createFiles.length) this.eventCallback('create',createFiles);
  104. }
  105. }
  106. }
  107. _removeAllFile(){
  108. let deleteFiles = [];
  109. for (const filePath in this._files) {
  110. deleteFiles.push(filePath);
  111. }
  112. this._files = {};
  113. if(deleteFiles.length) this.eventCallback('delete',deleteFiles);
  114. }
  115. }
  116. /**
  117. * 监听文件类
  118. */
  119. class WatchMgr{
  120. constructor(){
  121. /** @type [WatchFile] */
  122. this.eventListens = []
  123. this.watchFileBuffs = {}
  124. }
  125. /**
  126. *
  127. * @param {string} pathName
  128. * @param {WatchEventCallback} eventCallback
  129. * @returns {WatchFile}
  130. */
  131. addWatchPath(pathName,eventCallback) {
  132. let watchFile = new WatchFile(this,pathName,eventCallback);
  133. this.eventListens.push(watchFile);
  134. return watchFile;
  135. }
  136. checkAll() {
  137. for (let i = 0; i < this.eventListens.length; i++) {
  138. const watchFile = this.eventListens[i];
  139. watchFile.check();
  140. }
  141. }
  142. check(pathName) {
  143. for (let i = 0; i < this.eventListens.length; i++) {
  144. const watchFile = this.eventListens[i];
  145. if(watchFile.pathName == pathName){
  146. watchFile.check();
  147. break
  148. }
  149. }
  150. }
  151. removeWatch(pathName) {
  152. for (let i = 0; i < this.eventListens.length; i++) {
  153. const watchFile = this.eventListens[i];
  154. if(watchFile.pathName == pathName){
  155. this.eventListens.splice(i,1);
  156. break
  157. }
  158. }
  159. }
  160. }
  161. async function getDirAllFiles(dirPath ,result = {}) {
  162. return new Promise((resolve, reject )=>
  163. {
  164. fs.readdir(dirPath,(err,files)=>
  165. {
  166. if(err) return reject(err);
  167. let len = files.length;
  168. if(len == 0){
  169. resolve(result); // 没有文件
  170. return;
  171. }
  172. let cur_ind = 0;
  173. files.forEach((val) => {
  174. let fPath = path.join(dirPath, val);
  175. fs.stat(fPath,async (err,stat)=>
  176. {
  177. if(err) return reject(err);
  178. if (stat.isDirectory()) {
  179. result = await getDirAllFiles(fPath,result);
  180. } else if (stat.isFile()) {
  181. result[fPath] = stat ;
  182. }
  183. cur_ind ++;
  184. if(cur_ind == len) {
  185. resolve(result)
  186. }
  187. })
  188. });
  189. });
  190. })
  191. }
  192. exports.WatchMgr = WatchMgr;
  193. exports.WatchFile = WatchFile;