LjFileHelper.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. var request = require('request');
  2. var fs = require('fs');
  3. var ffmpeg = require('fluent-ffmpeg');
  4. var ljConst = require("../common/LjConst.js");
  5. var path = require('path');
  6. var logHelper = require("./LjLogHelper");
  7. var apiHelper = require("../helper/LjApiHelper");
  8. var httpHelper = require("../helper/LjHttpHelper");
  9. var os = require('os');
  10. /**
  11. * The helper of file
  12. */
  13. class LjFileHelper
  14. {
  15. /**
  16. * Download file
  17. * @param fileUrl the file url
  18. * @returns {boolean}
  19. */
  20. static downloadFile(fileUrl, opt)
  21. {
  22. if (fileUrl == null || fileUrl == "")
  23. {
  24. return "";
  25. }
  26. //获取文件属性对象
  27. var filePropObj = this.getFileProperty(fileUrl);
  28. if (filePropObj == null)
  29. {
  30. return false;
  31. }
  32. //视频处理
  33. if (filePropObj.fileType == ljConst.VIDEO_TYPE_M3U8)
  34. {
  35. //未加密视频本地存储路径
  36. var localFileDir = filePropObj.folderDir + filePropObj.fileName + ".mp4"
  37. //M3U8视频转换MP4
  38. ffmpeg(fileUrl).format(ljConst.VIDEO_TYPE_MP4)
  39. .on('start', function (err)
  40. {
  41. logHelper.info("【" + opt.lessonId + "】", "Starting down video:" + fileUrl);
  42. })
  43. .on('error', function (err)
  44. {
  45. logHelper.error("【" + opt.lessonId + "】", "Failed to down video[" + fileUrl + "]: " + err.message);
  46. })
  47. .on('end', function ()
  48. {
  49. //加密视频存储路径
  50. let localFileDirNew = filePropObj.folderDir + filePropObj.fileName + "_new" + ".mp4"
  51. logHelper.info("【" + opt.lessonId + "】", "Succeeded to down video:" + localFileDirNew);
  52. //加密存储
  53. LjFileHelper.writeEncryptedStream(localFileDir, localFileDirNew, function ()
  54. {
  55. LjFileHelper.updateDownloadResSize(opt);
  56. });
  57. if (fs.existsSync(localFileDir))
  58. {
  59. //删除文件(加密存储完成后删除未加密文件)
  60. fs.unlink(localFileDir, function (err)
  61. {
  62. if (err)
  63. {
  64. //删除失败
  65. logHelper.error("【" + opt.lessonId + "】", "Failed to delete video[" + err.message + "]:" + localFileDir);
  66. throw err;
  67. }
  68. else
  69. {
  70. logHelper.info("【" + opt.lessonId + "】", 'Succeeded to delete video:' + localFileDir)
  71. }
  72. });
  73. }
  74. })
  75. .save(localFileDir);
  76. }//有声读物处理
  77. else if (filePropObj.fileType == ljConst.VIDEO_TYPE_MP3)
  78. {
  79. //未加密有声读物本地存储地址
  80. var localFileDir = filePropObj.folderDir + filePropObj.fileName + "." + filePropObj.fileType;
  81. //读取有声读物
  82. request(fileUrl).pipe(fs.createWriteStream(localFileDir)).on('close', function ()
  83. {
  84. //加密有声读物存储地址
  85. var localFileDirNew = filePropObj.folderDir + filePropObj.fileName + "_new." + filePropObj.fileType;
  86. logHelper.info("【" + opt.lessonId + "】", "Succeeded to down audio:" + localFileDirNew);
  87. //加密有声读物
  88. LjFileHelper.writeEncryptedStream(localFileDir, localFileDirNew, function ()
  89. {
  90. /*//获取文件基本信息
  91. fs.stat(localFileDirNew, function (err, stats)
  92. {
  93. if (err == null)
  94. {
  95. //文件大小
  96. var fileSize = stats.size;
  97. LjFileHelper.updateDownloadResSize(opt);
  98. logHelper.info("Succeeded to encrypt audio:" + localFileDirNew + " with file size[" + fileSize + "]");
  99. }
  100. });*/
  101. LjFileHelper.updateDownloadResSize(opt);
  102. });
  103. if (fs.existsSync(localFileDir))
  104. {
  105. //删除文件(加密存储完成后删除未加密文件)
  106. fs.unlink(localFileDir, function (err)
  107. {
  108. if (err)
  109. {
  110. //删除失败
  111. logHelper.erro("【" + opt.lessonId + "】", "Failed to delete audio[" + err.message + "]:" + localFileDir);
  112. throw err;
  113. }
  114. else
  115. {
  116. logHelper.info("【" + opt.lessonId + "】", 'Succeeded to delete audio:' + localFileDir)
  117. }
  118. });
  119. }
  120. });
  121. }//图片处理
  122. else
  123. {
  124. //未加密图片本地存储地址
  125. var localFileDir = filePropObj.folderDir + filePropObj.fileName + "." + filePropObj.fileType;
  126. //读取图片
  127. request(fileUrl).pipe(fs.createWriteStream(localFileDir)).on('close', function ()
  128. {
  129. //加密图片存储地址
  130. var localFileDirNew = filePropObj.folderDir + filePropObj.fileName + "_new." + filePropObj.fileType;
  131. logHelper.info("【" + opt.lessonId + "】", "Succeeded to down image:" + localFileDirNew);
  132. //加密图片
  133. LjFileHelper.writeEncryptedStream(localFileDir, localFileDirNew, function ()
  134. {
  135. /* //获取文件基本信息
  136. fs.stat(localFileDirNew, function (err, stats)
  137. {
  138. if (err == null)
  139. {
  140. //文件大小
  141. var fileSize = stats.size;
  142. LjFileHelper.updateDownloadResSize(opt);
  143. logHelper.info("Succeeded to encrypt image:" + localFileDirNew + " with file size[" + fileSize + "]");
  144. }
  145. });*/
  146. LjFileHelper.updateDownloadResSize(opt);
  147. });
  148. if (fs.existsSync(localFileDir))
  149. {
  150. //删除文件(加密存储完成后删除未加密文件)
  151. fs.unlink(localFileDir, function (err)
  152. {
  153. if (err)
  154. {
  155. //删除失败
  156. logHelper.error("【" + opt.lessonId + "】", "Failed to delete image[" + err.message + "]:" + localFileDir);
  157. throw err;
  158. }
  159. else
  160. {
  161. logHelper.info("【" + opt.lessonId + "】", 'Succeeded to delete image:' + localFileDir)
  162. }
  163. });
  164. }
  165. });
  166. }
  167. }
  168. /**
  169. * Delete file
  170. * @param fileUrl the file url
  171. * @returns {boolean}
  172. */
  173. static delFile(fileUrl)
  174. {
  175. //获取文件属性对象
  176. var filePropObj = this.getFileProperty(fileUrl);
  177. if (filePropObj == null)
  178. {
  179. return false;
  180. }
  181. //视频处理
  182. if (filePropObj.fileType == ljConst.VIDEO_TYPE_M3U8)
  183. {
  184. //加密视频存储路径
  185. let localFileDirNew = filePropObj.folderDir + filePropObj.fileName + "_new" + ".mp4"
  186. if (fs.existsSync(localFileDirNew))
  187. {
  188. //删除加密文件
  189. fs.unlink(localFileDirNew, function (err)
  190. {
  191. if (err)
  192. {
  193. //删除失败
  194. logHelper.info("Failed to delete video[" + err.message + "]:" + localFileDir);
  195. throw err;
  196. }
  197. else
  198. {
  199. logHelper.info('Succeeded to delete video:' + localFileDir)
  200. }
  201. });
  202. }
  203. }//有声读物处理
  204. else if (filePropObj.fileType == ljConst.VIDEO_TYPE_MP3)
  205. {
  206. //未加密有声读物本地存储地址
  207. var localFileDir = filePropObj.folderDir + filePropObj.fileName + "." + filePropObj.fileType;
  208. //读取有声读物
  209. request(fileUrl).pipe(fs.createWriteStream(localFileDir)).on('close', function ()
  210. {
  211. //加密有声读物存储地址
  212. var localFileDirNew = filePropObj.folderDir + filePropObj.fileName + "_new." + filePropObj.fileType;
  213. if (fs.existsSync(localFileDirNew))
  214. {
  215. //删除文件(加密存储完成后删除未加密文件)
  216. fs.unlink(localFileDirNew, function (err)
  217. {
  218. if (err)
  219. {
  220. //删除失败
  221. logHelper.erro("【" + opt.lessonId + "】", "Failed to delete audio[" + err.message + "]:" + localFileDir);
  222. throw err;
  223. }
  224. else
  225. {
  226. logHelper.info("【" + opt.lessonId + "】", 'Succeeded to delete audio:' + localFileDir)
  227. }
  228. });
  229. }
  230. });
  231. }//图片处理
  232. else
  233. {
  234. //加密图片存储地址
  235. var localFileDirNew = filePropObj.folderDir + filePropObj.fileName + "_new." + filePropObj.fileType;
  236. if (fs.existsSync(localFileDirNew))
  237. {
  238. //删除加密文件
  239. fs.unlink(localFileDirNew, function (err)
  240. {
  241. if (err)
  242. {
  243. //删除失败
  244. logHelper.erro("Failed to delete image[" + err.message + "]:" + localFileDir);
  245. throw err;
  246. }
  247. else
  248. {
  249. logHelper.info('Succeeded to delete image:' + localFileDir)
  250. }
  251. });
  252. }
  253. }
  254. }
  255. /**
  256. * Gets file directory
  257. * @param fileName the file name
  258. * @returns {*|string} the file directory
  259. */
  260. static getFileDir(fileName)
  261. {
  262. var fileType = fileName.split(".")[1]
  263. var newFileDir = "";
  264. var platform = os.platform();
  265. if (platform == "win32")
  266. {
  267. if (fileType == "m3u8")
  268. {
  269. fileName = fileName.replace(".m3u8", "_new.mp4")
  270. newFileDir = ljConst.BASE_DIR + fileName.replace(/\*/g, '\\');
  271. }
  272. else
  273. {
  274. fileName = fileName.replace(".", "_new.")
  275. newFileDir = ljConst.BASE_DIR + fileName.replace(/\*/g, '\\');
  276. }
  277. }
  278. else if (platform == "darwin")
  279. {
  280. if (fileType == "m3u8")
  281. {
  282. fileName = fileName.replace(".m3u8", "_new.mp4")
  283. newFileDir = ljConst.BASE_DIR + fileName.replace(/\*/g, '/');
  284. }
  285. else
  286. {
  287. fileName = fileName.replace(".", "_new.")
  288. newFileDir = ljConst.BASE_DIR + fileName.replace(/\*/g, '/');
  289. }
  290. }
  291. return newFileDir;
  292. }
  293. /**
  294. * Makes directory
  295. * @param dirname the directory name
  296. * @returns {boolean} true or false
  297. */
  298. static mkdirsSync(dirname)
  299. {
  300. if (fs.existsSync(dirname))
  301. {
  302. return true;
  303. }
  304. else
  305. {
  306. if (this.mkdirsSync(path.dirname(dirname)))
  307. {
  308. fs.mkdirSync(dirname);
  309. return true;
  310. }
  311. }
  312. }
  313. /**
  314. * Gets the file property object
  315. * @param fileUrl the file URL
  316. * @returns {null}
  317. */
  318. static getFileProperty(fileUrl)
  319. {
  320. /*--文件夹目录-Begin--*/
  321. var folderDir = ljConst.BASE_DIR;
  322. var splitArr = fileUrl.split('/')
  323. var arrLen = splitArr.length;
  324. var platform = os.platform();
  325. if (platform == "win32")
  326. {
  327. for (var i = 3; i < arrLen - 1; i++)
  328. {
  329. folderDir += splitArr[i] + "\\";
  330. }
  331. }
  332. else if (platform == "darwin")
  333. {
  334. for (var i = 3; i < arrLen - 1; i++)
  335. {
  336. folderDir += splitArr[i] + "/";
  337. }
  338. }
  339. //创建文件夹目录
  340. this.mkdirsSync(folderDir);
  341. var filePropObj = {};
  342. filePropObj.folderDir = folderDir;
  343. //获取文件名称
  344. filePropObj.fileName = splitArr[arrLen - 1].split(".")[0];
  345. //获取文件类型
  346. filePropObj.fileType = splitArr[arrLen - 1].split(".")[1];
  347. /*--文件夹目录-Begin--*/
  348. return filePropObj;
  349. }
  350. /**
  351. * Gets the file property object
  352. * @param fileUrl the file URL
  353. * @returns {null}
  354. */
  355. static getFilePropertyNoMkdir(fileUrl)
  356. {
  357. if (fileUrl == null)
  358. {
  359. return;
  360. }
  361. /*--文件夹目录-Begin--*/
  362. var folderDir = ljConst.BASE_DIR;
  363. var splitArr = fileUrl.split('/')
  364. var arrLen = splitArr.length;
  365. var platform = os.platform();
  366. if (platform == "win32")
  367. {
  368. for (var i = 3; i < arrLen - 1; i++)
  369. {
  370. folderDir += splitArr[i] + "\\";
  371. }
  372. }
  373. else if (platform == "darwin")
  374. {
  375. for (var i = 3; i < arrLen - 1; i++)
  376. {
  377. folderDir += splitArr[i] + "/";
  378. }
  379. }
  380. var filePropObj = {};
  381. filePropObj.folderDir = folderDir;
  382. //获取文件名称
  383. filePropObj.fileName = splitArr[arrLen - 1].split(".")[0];
  384. //获取文件类型
  385. filePropObj.fileType = splitArr[arrLen - 1].split(".")[1];
  386. /*--文件夹目录-Begin--*/
  387. return filePropObj;
  388. }
  389. static Encrypted(c)
  390. {
  391. var b = '';
  392. for (var i = 0; i < c.length; i++)
  393. {
  394. var ch = c[i];
  395. if (ch == '0')
  396. {
  397. ch = 'f';
  398. }
  399. else if (ch == 'f')
  400. {
  401. ch = '0';
  402. }
  403. b = b + ch;
  404. //console.log(ch);
  405. }
  406. return b;
  407. }
  408. static Decrypted(c)
  409. {
  410. var b = '';
  411. for (var i = 0; i < c.length; i++)
  412. {
  413. var ch = c[i];
  414. if (ch == 'f')
  415. {
  416. ch = '0';
  417. }
  418. else if (ch == '0')
  419. {
  420. ch = 'f';
  421. }
  422. b = b + ch;
  423. //console.log(ch);
  424. }
  425. return b;
  426. }
  427. /**
  428. * Writes encrypted content, which is read from given source file, into given dest file
  429. * @param fSrc the source file
  430. * @param fDest the dest file
  431. */
  432. static writeEncryptedStream(fSrc, fDest, callback)
  433. {
  434. var rs = fs.createReadStream(fSrc);
  435. var ws = fs.createWriteStream(fDest);
  436. rs.on('data', function (chunk)
  437. {
  438. let b = new Buffer(chunk, 'hex');
  439. //console.log(b);
  440. let c = b.toString('hex');
  441. c = LjFileHelper.encryptedContent(c);
  442. let d = Buffer.from(c, 'hex');
  443. ws.write(d);
  444. });
  445. rs.on('end', function ()
  446. {
  447. ws.end();
  448. logHelper.info('Succeeded in writing into encrypted stream[' + fDest + '].');
  449. callback();
  450. });
  451. rs.on('error', function (err)
  452. {
  453. logHelper.error(err.stack);
  454. });
  455. }
  456. /**
  457. * Encrypts the content
  458. * @param c the content to be encrypted
  459. * @return the encrypted content
  460. */
  461. static encryptedContent(c)
  462. {
  463. var b = '';
  464. for (var i = 0; i < c.length; i++)
  465. {
  466. var ch = c[i];
  467. if (ch == '0')
  468. {
  469. ch = 'f';
  470. }
  471. else if (ch == 'f')
  472. {
  473. ch = '0';
  474. }
  475. else if (ch == '1')
  476. {
  477. ch = 'e';
  478. }
  479. else if (ch == 'e')
  480. {
  481. ch = '1';
  482. }
  483. else if (ch == '2')
  484. {
  485. ch = 'd';
  486. }
  487. else if (ch == 'd')
  488. {
  489. ch = '2';
  490. }
  491. else if (ch == '3')
  492. {
  493. ch = 'c';
  494. }
  495. else if (ch == 'c')
  496. {
  497. ch = '3';
  498. }
  499. b = b + ch;
  500. //console.log(ch);
  501. }
  502. return b;
  503. }
  504. /**
  505. * Decrypts the encrypted content
  506. * @param c the encrypted content
  507. * @return the decrypted content
  508. */
  509. static decryptedContent(c)
  510. {
  511. var b = '';
  512. for (var i = 0; i < c.length; i++)
  513. {
  514. var ch = c[i];
  515. if (ch == 'f')
  516. {
  517. ch = '0';
  518. }
  519. else if (ch == '0')
  520. {
  521. ch = 'f';
  522. }
  523. else if (ch == 'e')
  524. {
  525. ch = '1';
  526. }
  527. else if (ch == '1')
  528. {
  529. ch = 'e';
  530. }
  531. else if (ch == 'd')
  532. {
  533. ch = '2';
  534. }
  535. else if (ch == '2')
  536. {
  537. ch = 'd';
  538. }
  539. else if (ch == 'c')
  540. {
  541. ch = '3';
  542. }
  543. else if (ch == '3')
  544. {
  545. ch = 'c';
  546. }
  547. b = b + ch;
  548. //console.log(ch);
  549. }
  550. return b;
  551. }
  552. /**
  553. * Reads decrypted content, which is read from given encrypted file, into HTTP response stream
  554. * @param res the HTTP response stream
  555. * @param fDest the encrypted file
  556. */
  557. static readDecryptedStream(res, fDest, contentType)
  558. {
  559. res.writeHead(200, {'Content-Type': contentType});
  560. var rs = fs.createReadStream(fDest);
  561. rs.on('data', function (chunk)
  562. {
  563. let b = new Buffer(chunk, 'hex');
  564. let c = b.toString('hex');
  565. c = LjFileHelper.decryptedContent(c);
  566. let d = Buffer.from(c, 'hex');
  567. res.write(d);
  568. });
  569. rs.on('end', function ()
  570. {
  571. res.end();
  572. logHelper.log('Succeeded in reading from decrypted stream[' + fDest + '].');
  573. });
  574. rs.on('error', function (err)
  575. {
  576. logHelper.log(err.stack);
  577. });
  578. }
  579. static updateDownloadResSize(opt)
  580. {
  581. //return false;
  582. opt.url = apiHelper.getApiForUpdateResSize(opt.lessonId);
  583. opt.method = "PUT";
  584. opt.path = "/callback/download/update/resource/size";
  585. httpHelper.request(opt, function (error, res, body)
  586. {
  587. var retObj = JSON.parse(body);
  588. if (retObj.code == 200)
  589. {
  590. logHelper.info("Succeed to call api[" + opt.path + "]");
  591. return true;
  592. }
  593. else
  594. {
  595. logHelper.error("Failed to call api[" + opt.path + "],caused by error[" + retObj.message + "]");
  596. return false;
  597. }
  598. });
  599. }
  600. /**
  601. * Is video
  602. * @param dir the directory
  603. * @returns {boolean}
  604. */
  605. isVideo(dir)
  606. {
  607. if (dir == "")
  608. {
  609. return false;
  610. }
  611. var fileType = dir.split(".")[1];
  612. if ("m3u8" == fileType)
  613. {
  614. return true;
  615. }
  616. return false;
  617. }
  618. }
  619. module.exports = LjFileHelper;