file.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. const fs = require('fs');
  2. const path = require('path');
  3. const {logger} = require("./logger");
  4. const {rustConfig} = require("../config");
  5. const http = require("./http");
  6. const readline = require('readline');
  7. // 检查目录是否存在,不存在新建
  8. function checkPathSync(filePath) {
  9. // 将相对路径转换为绝对路径
  10. const directoryPath = path.resolve(filePath);
  11. // 同步检查目录是否存在
  12. if (!fs.existsSync(directoryPath)) {
  13. // 目录不存在,需要创建它
  14. logger.info('目录不存在,正在创建...');
  15. try {
  16. // 同步创建目录
  17. fs.mkdirSync(directoryPath, {recursive: true});
  18. logger.info('目录已成功创建');
  19. } catch (error) {
  20. // 创建目录失败
  21. logger.error('创建目录失败:', error);
  22. return false
  23. }
  24. } else {
  25. // 目录已存在
  26. // logger.info('目录已存在');
  27. }
  28. return true
  29. }
  30. // 检查文件是否存在
  31. function checkFilePath(filePath) {
  32. // 同步检查文件是否存在
  33. const directoryPath = path.resolve(filePath);
  34. if (fs.existsSync(directoryPath)) {
  35. logger.info('文件存在:' + filePath);
  36. return true
  37. } else {
  38. logger.error('文件不存在:' + filePath);
  39. return false
  40. }
  41. }
  42. // 创建文件
  43. function writeFile(filePath, text, breakFun) {
  44. fs.writeFile(filePath, text, (err) => {
  45. if (err) {
  46. logger.error('写入文件时发生错误:', err);
  47. breakFun(err)
  48. } else {
  49. logger.log('文件已成功写入!');
  50. breakFun(null, true)
  51. }
  52. });
  53. }
  54. // 同步复制
  55. function copyFileSync(source, destination) {
  56. try {
  57. // 同步复制文件
  58. fs.copyFileSync(source, destination);
  59. // logger.info("成功");
  60. return true;
  61. } catch (err) {
  62. return false;
  63. // logger.info("失败");
  64. }
  65. }
  66. // // 读取日志(倒叙)指定行数
  67. // function readLastNLines(filePath, n) {
  68. // const content = fs.readFileSync(filePath, 'utf8');
  69. // const lines = content.trim().split('\n');
  70. // const lastNLines = lines.slice(-n).join('\n');
  71. //
  72. // // 此处处理或输出最后n行的内容
  73. // // console.log(lastNLines);
  74. // return lastNLines.split("\n").reverse();
  75. // }
  76. // 下载文件
  77. function dowFile(url, fileName, dowPath, headers) {
  78. return http.dowFile(url, fileName, dowPath, headers)
  79. }
  80. // 删除文件
  81. function delFile() {
  82. }
  83. /***********************************/
  84. function getLastFile(dirPath, number, callback) {
  85. fs.readdir(dirPath, (err, files) => {
  86. // logger.info(`---------------------4`)
  87. if (err) {
  88. logger.error(`无法列出目录。`, err);
  89. // process.exit(1);
  90. } else {
  91. let fileList = [];
  92. //提示日志默认是根据日志生成的,拿到的files 也是 按照生成日志排序的,所以
  93. // 按照倒叙获取,如果一个文件内容满足num,就不在继续查询,如果不够,继续获取文件
  94. for (file of files) {
  95. const filePath = path.join(dirPath, file);
  96. // logger.info("所有文件:",filePath)
  97. fs.promises.stat(filePath).then((stats) =>{
  98. if (stats.isFile()) {
  99. let tt = stats.birthtime.getTime()
  100. fileList.push({name: file, time: tt});
  101. logger.info("----------------所有文件:" + filePath + "时间:" + tt)
  102. }
  103. });
  104. }
  105. for (a of fileList) {
  106. const filePath = path.join(dirPath, a.name);
  107. logger.info("复查:" + filePath + "\ttime:" + a.time)
  108. }
  109. fileList.sort((a, b) => b.time - a.time);
  110. for (a of fileList) {
  111. const filePath = path.join(dirPath, a.name);
  112. logger.info("排序:" + filePath + "\ttime:" + a.time)
  113. }
  114. let lastFileList = fileList.slice(0, number != -1 ? number : fileList.length);
  115. let lastFileNameList = lastFileList.map((item) => item.name);
  116. callback(lastFileNameList.reverse(), lastFileList.reverse());
  117. }
  118. });
  119. }
  120. // 读取日志(倒叙)指定行数
  121. async function readLastNLines(dirPath, filePathList, n) {
  122. try {
  123. const fileList = [];
  124. var count = 0;
  125. for (var i = filePathList.length - 1; i >= 0; i--) {
  126. var item = filePathList[i];
  127. const filepath = `${dirPath}/${item}`;
  128. logger.info("日志文件:" + filepath)
  129. //老版本 加载整个文件
  130. // const content = fs.readFileSync(`${filepath}`, "utf8");
  131. // let lines = content.trim().split("\n");
  132. //新版本:流式读取和逐行处理
  133. const lines = await readLinesFromEnd(filepath, n - count);
  134. // logger.info("日:" + lines)
  135. fileList.push({
  136. filePath: item,
  137. lines: lines,
  138. })
  139. count += lines.length;
  140. if (count >= n) {
  141. break
  142. }
  143. }
  144. fileList.reverse();
  145. const allFile = [].concat(...fileList.map((item) => item.lines));
  146. const lastNLines = allFile.slice(-n).reverse();
  147. return lastNLines;
  148. } catch (e) {
  149. logger.info('读取文件发生异常咯~~2', e);
  150. }
  151. }
  152. async function getRecentLogs(dirPath, logFiles, requiredLogs = 100) {
  153. let logs = [];
  154. try {
  155. let logsSize = requiredLogs
  156. for (const file of logFiles) {
  157. let log = [];
  158. // 创建文件流
  159. const filepath = `${dirPath}/${file}`;
  160. const fileStream = fs.createReadStream(filepath);
  161. const rl = readline.createInterface({
  162. input: fileStream,
  163. crlfDelay: Infinity
  164. });
  165. for await (const line of rl) {
  166. log.push(line);
  167. }
  168. for (const l of log.reverse()) {
  169. logs.push(l);
  170. if (logs.length >= requiredLogs) {
  171. break
  172. }
  173. }
  174. }
  175. } catch (e) {
  176. logger.info('读取文件发生异常咯~~~', e.message);
  177. }
  178. return logs; // 如果日志不够500条,返回现有日志
  179. }
  180. async function getLatestLogEntries(dirPath, logFiles, requiredLogs = 100) {
  181. const MAX_LOGS = requiredLogs;
  182. let logs = [];
  183. try {
  184. for (const file of logFiles) {
  185. // 创建文件流
  186. const filepath = `${dirPath}/${file}`;
  187. logger.info('文件~~~', filepath);
  188. // const readStream = fs.createReadStream(filepath, { highWaterMark: 2 * 1024 * 1024 }); // 每次读取10MB
  189. // let buffer = '';
  190. //
  191. // logs = await new Promise((resolve,reject) =>{
  192. // // 逐块读取并处理数据
  193. // readStream.on('data', (chunk) => {
  194. // buffer += chunk.toString(); // 将块数据转成字符串
  195. // const lines = buffer.split('\n'); // 按行拆分
  196. //
  197. // // 处理新行
  198. // for (let i = 0; i < lines.length - 1; i++) {
  199. // logs.unshift(lines[i]); // 添加到数组前面以实现倒序
  200. // // logs.push(lines[i]); // 添加到数组前面以实现倒序
  201. // logger.info( lines[i]);
  202. //
  203. // if (logs.length >= MAX_LOGS) break; // 达到所需条数则停止
  204. // }
  205. //
  206. // // 保留未处理的最后一行(如果是半行,继续留在缓冲区)
  207. // buffer = lines[lines.length - 1];
  208. // });
  209. //
  210. // // 读取结束后处理剩余缓冲区
  211. // readStream.on('end', () => {
  212. // if (buffer) {
  213. // logs.unshift(buffer); // 加入最后的未处理行
  214. // }
  215. // resolve(logs); // 完成时解析 Promise
  216. // });
  217. //
  218. // readStream.on('error', (err) => {
  219. // reject(err); // 如果出错,拒绝 Promise
  220. // });
  221. //
  222. // // 确保在获取到足够记录时停止读取
  223. // readStream.on('close', () => {
  224. // if (logs.length >= MAX_LOGS) {
  225. // readStream.destroy(); // 如果达到了最大记录数,停止读取流
  226. // }else{
  227. // logger.info('----继续读取~~',logs.length);
  228. // }
  229. // });
  230. // })
  231. // // logger.info( logs);
  232. // // 如果已达到所需行数,停止继续读取文件
  233. // if (logs.length >= MAX_LOGS) {
  234. // break; // 退出循环
  235. // }
  236. // 文件信息
  237. const stats = fs.statSync(filepath); // 获取文件信息
  238. let position = stats.size; // 从文件末尾开始
  239. const chunkSize = 1 * 1024 * 1024; // 每次读取1MB
  240. // 文件读取方式
  241. while (position > 0 && logs.length < MAX_LOGS) {
  242. const readSize = Math.min(chunkSize, position); // 确保不读取超过文件的大小
  243. const readStream = fs.createReadStream(filepath, {
  244. start: position - readSize, // 从当前位置向上读取
  245. end: position - 1, // 读取到当前位置
  246. highWaterMark: readSize // 一次性读取指定大小
  247. });
  248. // 缓存
  249. let buffer = '';
  250. await new Promise((resolve, reject) => {
  251. readStream.on('data', (chunk) => {
  252. buffer = chunk.toString() + buffer; // 新数据放在前面
  253. });
  254. readStream.on('end', () => {
  255. // 按行处理数据
  256. const lines = buffer.split('\n');
  257. while (lines.length > 0) {
  258. const line = lines.pop(); // 取出最新的一行
  259. if (line) {
  260. logs.unshift(line); // 添加到日志数组开头
  261. // logger.info(line);
  262. if (logs.length >= requiredLogs) break; // 达到要求的条数
  263. }
  264. }
  265. resolve();
  266. });
  267. readStream.on('error', (err) => {
  268. console.error('读取文件发生错误:', err);
  269. reject(err); // 确保不会因错误而卡住
  270. });
  271. readStream.on('close', () => {
  272. if (logs.length >= MAX_LOGS) {
  273. readStream.destroy(); // 如果达到了最大记录数,停止读取流
  274. } else {
  275. logger.info('----继续读取~~', logs.length);
  276. }
  277. });
  278. });
  279. position -= readSize; // 递减位置
  280. }
  281. // 如果已达到所需行数,停止继续读取文件
  282. if (logs.length >= MAX_LOGS) {
  283. break; // 退出循环
  284. }
  285. }
  286. } catch (e) {
  287. logger.info('读取文件发生异常咯~~~', e.message);
  288. }
  289. // logger.info( logs);
  290. return logs.reverse(); // 如果日志不够500条,返回现有日志
  291. }
  292. async function readLinesFromEnd(filePath, maxLines) {
  293. const lines = [];
  294. const fileSize = fs.statSync(filePath).size;
  295. const chunkSize = 1024 * 10 * 5; // 每次读取的块大小
  296. try {
  297. if (fileSize < chunkSize) {
  298. const fileStream = fs.createReadStream(filePath, {
  299. start: Math.max(0, 0),
  300. end: fileSize
  301. });
  302. let buffer = '';
  303. for await (const chunk of fileStream) {
  304. buffer = chunk + buffer;
  305. let lineEndIndex = buffer.length;
  306. while (lineEndIndex >= 0 && lines.length < maxLines) {
  307. const lineStartIndex = buffer.lastIndexOf('\n', lineEndIndex - 1);
  308. if (lineStartIndex === -1) {
  309. break;
  310. }
  311. const line = buffer.substring(lineStartIndex + 1, lineEndIndex).trim();
  312. if (line) {
  313. lines.push(line);
  314. }
  315. lineEndIndex = lineStartIndex;
  316. }
  317. if (lines.length >= maxLines) {
  318. break;
  319. }
  320. }
  321. } else {
  322. let forNum = (fileSize / chunkSize) + 1;
  323. for (let i = 1; i <= forNum; i++) {
  324. let position = fileSize - chunkSize * i;
  325. let z = position + chunkSize
  326. const fileStream = fs.createReadStream(filePath, {
  327. start: Math.max(position, 0),
  328. end: z
  329. });
  330. let buffer = '';
  331. for await (const chunk of fileStream) {
  332. buffer = chunk + buffer;
  333. let lineEndIndex = buffer.length;
  334. while (lineEndIndex >= 0 && lines.length < maxLines) {
  335. const lineStartIndex = buffer.lastIndexOf('\n', lineEndIndex - 1);
  336. if (lineStartIndex === -1) {
  337. break;
  338. }
  339. const line = buffer.substring(lineStartIndex + 1, lineEndIndex).trim();
  340. if (line) {
  341. lines.push(line);
  342. }
  343. lineEndIndex = lineStartIndex;
  344. }
  345. if (lines.length >= maxLines) {
  346. break;
  347. }
  348. position -= chunkSize;
  349. if (position < 0) {
  350. position = 0;
  351. }
  352. }
  353. }
  354. }
  355. } catch (e) {
  356. logger.info('读取文件发生异常咯~~~1', e.message);
  357. }
  358. return lines.reverse();
  359. }
  360. async function readLines(filePath, maxLines) {
  361. const fileStream = fs.createReadStream(filePath);
  362. const rl = readline.createInterface({
  363. input: fileStream,
  364. crlfDelay: Infinity
  365. });
  366. const lines = [];
  367. for await (const line of rl) {
  368. lines.push(line);
  369. if (lines.length >= maxLines) {
  370. break;
  371. }
  372. }
  373. rl.close();
  374. fileStream.close();
  375. return lines;
  376. }
  377. module.exports = {
  378. dowFile,
  379. checkFilePath,
  380. checkPathSync,
  381. writeFile,
  382. copyFileSync,
  383. readLastNLines,
  384. getLatestLogEntries,
  385. getRecentLogs,
  386. getLastFile
  387. }