var request = require('request'); var fs = require('fs'); var ffmpeg = require('fluent-ffmpeg'); var ljConst = require("../common/LjConst.js"); var path = require('path'); var logHelper = require("./LjLogHelper"); var apiHelper = require("../helper/LjApiHelper"); var httpHelper = require("../helper/LjHttpHelper"); var os = require('os'); /** * The helper of file */ class LjFileHelper { /** * Download file * @param fileUrl the file url * @returns {boolean} */ static downloadFile(fileUrl, opt) { if(fileUrl == null || fileUrl=="") { return ""; } //获取文件属性对象 var filePropObj = this.getFileProperty(fileUrl); if (filePropObj == null) { return false; } //视频处理 if (filePropObj.fileType == ljConst.VIDEO_TYPE_M3U8) { //未加密视频本地存储路径 var localFileDir = filePropObj.folderDir + filePropObj.fileName + ".mp4" //M3U8视频转换MP4 ffmpeg(fileUrl).format(ljConst.VIDEO_TYPE_MP4) .on('start', function (err) { logHelper.info("【"+opt.lessonId+"】","Starting down video:" + fileUrl); }) .on('error', function (err) { logHelper.error("【"+opt.lessonId+"】","Failed to down video[" + fileUrl + "]: " + err.message); }) .on('end', function () { //加密视频存储路径 let localFileDirNew = filePropObj.folderDir + filePropObj.fileName + "_new" + ".mp4" logHelper.info("【"+opt.lessonId+"】","Succeeded to down video:" + localFileDirNew); //加密存储 LjFileHelper.writeEncryptedStream(localFileDir, localFileDirNew, function () { LjFileHelper.updateDownloadResSize(opt); }); //删除文件(加密存储完成后删除未加密文件) fs.unlink(localFileDir, function (err) { if (err) { //删除失败 logHelper.error("【"+opt.lessonId+"】","Failed to delete video[" + err.message + "]:" + localFileDir); throw err; } else { logHelper.info("【"+opt.lessonId+"】",'Succeeded to delete video:' + localFileDir) } }) }) .save(localFileDir); }//有声读物处理 else if (filePropObj.fileType == ljConst.VIDEO_TYPE_MP3) { //未加密有声读物本地存储地址 var localFileDir = filePropObj.folderDir + filePropObj.fileName + "." + filePropObj.fileType; //读取有声读物 request(fileUrl).pipe(fs.createWriteStream(localFileDir)).on('close', function () { //加密有声读物存储地址 var localFileDirNew = filePropObj.folderDir + filePropObj.fileName + "_new." + filePropObj.fileType; logHelper.info("【"+opt.lessonId+"】","Succeeded to down audio:" + localFileDirNew); //加密有声读物 LjFileHelper.writeEncryptedStream(localFileDir, localFileDirNew, function () { /*//获取文件基本信息 fs.stat(localFileDirNew, function (err, stats) { if (err == null) { //文件大小 var fileSize = stats.size; LjFileHelper.updateDownloadResSize(opt); logHelper.info("Succeeded to encrypt audio:" + localFileDirNew + " with file size[" + fileSize + "]"); } });*/ LjFileHelper.updateDownloadResSize(opt); }); //删除文件(加密存储完成后删除未加密文件) fs.unlink(localFileDir, function (err) { if (err) { //删除失败 logHelper.erro("【"+opt.lessonId+"】","Failed to delete audio[" + err.message + "]:" + localFileDir); throw err; } else { logHelper.info("【"+opt.lessonId+"】",'Succeeded to delete audio:' + localFileDir) } }) }); }//图片处理 else { //未加密图片本地存储地址 var localFileDir = filePropObj.folderDir + filePropObj.fileName + "." + filePropObj.fileType; //读取图片 request(fileUrl).pipe(fs.createWriteStream(localFileDir)).on('close', function () { //加密图片存储地址 var localFileDirNew = filePropObj.folderDir + filePropObj.fileName + "_new." + filePropObj.fileType; logHelper.info("【"+opt.lessonId+"】","Succeeded to down image:" + localFileDirNew); //加密图片 LjFileHelper.writeEncryptedStream(localFileDir, localFileDirNew, function () { /* //获取文件基本信息 fs.stat(localFileDirNew, function (err, stats) { if (err == null) { //文件大小 var fileSize = stats.size; LjFileHelper.updateDownloadResSize(opt); logHelper.info("Succeeded to encrypt image:" + localFileDirNew + " with file size[" + fileSize + "]"); } });*/ LjFileHelper.updateDownloadResSize(opt); }); //删除文件(加密存储完成后删除未加密文件) fs.unlink(localFileDir, function (err) { if (err) { //删除失败 logHelper.error("【"+opt.lessonId+"】","Failed to delete image[" + err.message + "]:" + localFileDir); throw err; } else { logHelper.info("【"+opt.lessonId+"】",'Succeeded to delete image:' + localFileDir) } }) }); } } /** * Delete file * @param fileUrl the file url * @returns {boolean} */ static delFile(fileUrl) { //获取文件属性对象 var filePropObj = this.getFileProperty(fileUrl); if (filePropObj == null) { return false; } //视频处理 if (filePropObj.fileType == ljConst.VIDEO_TYPE_M3U8) { //加密视频存储路径 let localFileDirNew = filePropObj.folderDir + filePropObj.fileName + "_new" + ".mp4" //删除加密文件 fs.unlink(localFileDirNew, function (err) { if (err) { //删除失败 logHelper.info("Failed to delete video[" + err.message + "]:" + localFileDir); throw err; } else { logHelper.info('Succeeded to delete video:' + localFileDir) } }) }//有声读物处理 else if (filePropObj.fileType == ljConst.VIDEO_TYPE_MP3) { //未加密有声读物本地存储地址 var localFileDir = filePropObj.folderDir + filePropObj.fileName + "." + filePropObj.fileType; //读取有声读物 request(fileUrl).pipe(fs.createWriteStream(localFileDir)).on('close', function () { //加密有声读物存储地址 var localFileDirNew = filePropObj.folderDir + filePropObj.fileName + "_new." + filePropObj.fileType; //删除文件(加密存储完成后删除未加密文件) fs.unlink(localFileDirNew, function (err) { if (err) { //删除失败 logHelper.erro("【"+opt.lessonId+"】","Failed to delete audio[" + err.message + "]:" + localFileDir); throw err; } else { logHelper.info("【"+opt.lessonId+"】",'Succeeded to delete audio:' + localFileDir) } }) }); }//图片处理 else { //加密图片存储地址 var localFileDirNew = filePropObj.folderDir + filePropObj.fileName + "_new." + filePropObj.fileType; //删除加密文件 fs.unlink(localFileDirNew, function (err) { if (err) { //删除失败 logHelper.erro("Failed to delete image[" + err.message + "]:" + localFileDir); throw err; } else { logHelper.info('Succeeded to delete image:' + localFileDir) } }) } } /** * Gets file directory * @param fileName the file name * @returns {*|string} the file directory */ static getFileDir(fileName) { var fileType = fileName.split(".")[1] var newFileDir = ""; var platform = os.platform(); if (platform == "win32") { if (fileType == "m3u8") { fileName = fileName.replace(".m3u8", "_new.mp4") newFileDir = ljConst.BASE_DIR + fileName.replace(/\*/g, '\\'); } else { fileName = fileName.replace(".", "_new.") newFileDir = ljConst.BASE_DIR + fileName.replace(/\*/g, '\\'); } } else if (platform == "darwin") { if (fileType == "m3u8") { fileName = fileName.replace(".m3u8", "_new.mp4") newFileDir = ljConst.BASE_DIR + fileName.replace(/\*/g, '/'); } else { fileName = fileName.replace(".", "_new.") newFileDir = ljConst.BASE_DIR + fileName.replace(/\*/g, '/'); } } return newFileDir; } /** * Makes directory * @param dirname the directory name * @returns {boolean} true or false */ static mkdirsSync(dirname) { if (fs.existsSync(dirname)) { return true; } else { if (this.mkdirsSync(path.dirname(dirname))) { fs.mkdirSync(dirname); return true; } } } /** * Gets the file property object * @param fileUrl the file URL * @returns {null} */ static getFileProperty(fileUrl) { /*--文件夹目录-Begin--*/ var folderDir = ljConst.BASE_DIR; var splitArr = fileUrl.split('/') var arrLen = splitArr.length; var platform = os.platform(); if (platform == "win32") { for (var i = 3; i < arrLen - 1; i++) { folderDir += splitArr[i] + "\\"; } } else if(platform == "darwin") { for (var i = 3; i < arrLen - 1; i++) { folderDir += splitArr[i] + "/"; } } //创建文件夹目录 this.mkdirsSync(folderDir); var filePropObj = {}; filePropObj.folderDir = folderDir; //获取文件名称 filePropObj.fileName = splitArr[arrLen - 1].split(".")[0]; //获取文件类型 filePropObj.fileType = splitArr[arrLen - 1].split(".")[1]; /*--文件夹目录-Begin--*/ return filePropObj; } /** * Gets the file property object * @param fileUrl the file URL * @returns {null} */ static getFilePropertyNoMkdir(fileUrl) { if(fileUrl == null) { return; } /*--文件夹目录-Begin--*/ var folderDir = ljConst.BASE_DIR; var splitArr = fileUrl.split('/') var arrLen = splitArr.length; var platform = os.platform(); if (platform == "win32") { for (var i = 3; i < arrLen - 1; i++) { folderDir += splitArr[i] + "\\"; } } else if(platform == "darwin") { for (var i = 3; i < arrLen - 1; i++) { folderDir += splitArr[i] + "/"; } } var filePropObj = {}; filePropObj.folderDir = folderDir; //获取文件名称 filePropObj.fileName = splitArr[arrLen - 1].split(".")[0]; //获取文件类型 filePropObj.fileType = splitArr[arrLen - 1].split(".")[1]; /*--文件夹目录-Begin--*/ return filePropObj; } static Encrypted(c) { var b = ''; for (var i = 0; i < c.length; i++) { var ch = c[i]; if (ch == '0') { ch = 'f'; } else if (ch == 'f') { ch = '0'; } b = b + ch; //console.log(ch); } return b; } static Decrypted(c) { var b = ''; for (var i = 0; i < c.length; i++) { var ch = c[i]; if (ch == 'f') { ch = '0'; } else if (ch == '0') { ch = 'f'; } b = b + ch; //console.log(ch); } return b; } /** * Writes encrypted content, which is read from given source file, into given dest file * @param fSrc the source file * @param fDest the dest file */ static writeEncryptedStream(fSrc, fDest, callback) { var rs = fs.createReadStream(fSrc); var ws = fs.createWriteStream(fDest); rs.on('data', function (chunk) { let b = new Buffer(chunk, 'hex'); //console.log(b); let c = b.toString('hex'); c = LjFileHelper.encryptedContent(c); let d = Buffer.from(c, 'hex'); ws.write(d); }); rs.on('end', function () { ws.end(); logHelper.info('Succeeded in writing into encrypted stream[' + fDest + '].'); callback(); }); rs.on('error', function (err) { logHelper.error(err.stack); }); } /** * Encrypts the content * @param c the content to be encrypted * @return the encrypted content */ static encryptedContent(c) { var b = ''; for (var i = 0; i < c.length; i++) { var ch = c[i]; if (ch == '0') { ch = 'f'; } else if (ch == 'f') { ch = '0'; } else if (ch == '1') { ch = 'e'; } else if (ch == 'e') { ch = '1'; } else if (ch == '2') { ch = 'd'; } else if (ch == 'd') { ch = '2'; } else if (ch == '3') { ch = 'c'; } else if (ch == 'c') { ch = '3'; } b = b + ch; //console.log(ch); } return b; } /** * Decrypts the encrypted content * @param c the encrypted content * @return the decrypted content */ static decryptedContent(c) { var b = ''; for (var i = 0; i < c.length; i++) { var ch = c[i]; if (ch == 'f') { ch = '0'; } else if (ch == '0') { ch = 'f'; } else if (ch == 'e') { ch = '1'; } else if (ch == '1') { ch = 'e'; } else if (ch == 'd') { ch = '2'; } else if (ch == '2') { ch = 'd'; } else if (ch == 'c') { ch = '3'; } else if (ch == '3') { ch = 'c'; } b = b + ch; //console.log(ch); } return b; } /** * Reads decrypted content, which is read from given encrypted file, into HTTP response stream * @param res the HTTP response stream * @param fDest the encrypted file */ static readDecryptedStream(res, fDest, contentType) { res.writeHead(200, {'Content-Type': contentType}); var rs = fs.createReadStream(fDest); rs.on('data', function (chunk) { let b = new Buffer(chunk, 'hex'); let c = b.toString('hex'); c = LjFileHelper.decryptedContent(c); let d = Buffer.from(c, 'hex'); res.write(d); }); rs.on('end', function () { res.end(); logHelper.log('Succeeded in reading from decrypted stream[' + fDest + '].'); }); rs.on('error', function (err) { logHelper.log(err.stack); }); } static updateDownloadResSize(opt) { //return false; opt.url = apiHelper.getApiForUpdateResSize(opt.lessonId); opt.method = "PUT"; opt.path = "/callback/download/update/resource/size"; httpHelper.request(opt, function (error, res, body) { var retObj = JSON.parse(body); if (retObj.code == 200) { logHelper.info("Succeed to call api[" + opt.path + "]"); return true; } else { logHelper.error("Failed to call api[" + opt.path + "],caused by error[" + retObj.message + "]"); return false; } }); } /** * Is video * @param dir the directory * @returns {boolean} */ isVideo(dir) { if (dir == "") { return false; } var fileType = dir.split(".")[1]; if ("m3u8" == fileType) { return true; } return false; } } module.exports = LjFileHelper;