LjFileHelper.js 18 KB

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