|
@@ -0,0 +1,411 @@
|
|
|
+var request = require('request');
|
|
|
+var fs = require('fs');
|
|
|
+var ffmpeg = require('fluent-ffmpeg');
|
|
|
+var ljConst = require("../const/LjConst.js");
|
|
|
+var path = require('path');
|
|
|
+var logHelper = require("./LjLogHelper");
|
|
|
+
|
|
|
+/**
|
|
|
+ * The helper of file
|
|
|
+ */
|
|
|
+class LjFileHelper
|
|
|
+{
|
|
|
+ /**
|
|
|
+ * Download file
|
|
|
+ * @param fileUrl the file url
|
|
|
+ * @returns {boolean}
|
|
|
+ */
|
|
|
+ static downloadFile(fileUrl)
|
|
|
+ {
|
|
|
+ //获取文件属性对象
|
|
|
+ var filePropObj = this.getFileProperty(fileUrl);
|
|
|
+ if (filePropObj == null)
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ //视频处理
|
|
|
+ if (filePropObj.fileType == ljConst.constBase.VIDEO_TYPE_M3U8)
|
|
|
+ {
|
|
|
+ //未加密视频本地存储路径
|
|
|
+ var localFileDir = filePropObj.folderDir + filePropObj.fileName + ".mp4"
|
|
|
+
|
|
|
+ //M3U8视频转换MP4
|
|
|
+ ffmpeg(fileUrl).format(ljConst.constBase.VIDEO_TYPE_MP4)
|
|
|
+ .on('start', function (err)
|
|
|
+ {
|
|
|
+ logHelper.info("Starting down video:" + fileUrl);
|
|
|
+ })
|
|
|
+ .on('error', function (err)
|
|
|
+ {
|
|
|
+ logHelper.error("Succeeded to down video[" + fileUrl + "]: " + err.message);
|
|
|
+ })
|
|
|
+ .on('end', function ()
|
|
|
+ {
|
|
|
+ //加密视频存储路径
|
|
|
+ let localFileDirNew = filePropObj.folderDir + filePropObj.fileName + "_new" + ".mp4"
|
|
|
+ logHelper.info("Succeeded to down video:" + localFileDirNew);
|
|
|
+
|
|
|
+ //加密存储
|
|
|
+ this.writeEncryptedStream(localFileDir, localFileDirNew);
|
|
|
+ console.info("Succeeded to encrypt video:" + localFileDirNew);
|
|
|
+
|
|
|
+ //删除文件(加密存储完成后删除未加密文件)
|
|
|
+ fs.unlink(localFileDir, function (err)
|
|
|
+ {
|
|
|
+ if (err)
|
|
|
+ {
|
|
|
+ //删除失败
|
|
|
+ logHelper.info("Failed to delete video[" + err.message + "]:" + localFileDir);
|
|
|
+ throw err;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ logHelper.info('Succeeded to delete video:' + localFileDir)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ .save(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("Succeeded to down image:" + localFileDirNew);
|
|
|
+
|
|
|
+ //加密图片
|
|
|
+ LjFileHelper.writeEncryptedStream(localFileDir, localFileDirNew);
|
|
|
+
|
|
|
+ logHelper.info("Succeeded to image video:" + localFileDirNew);
|
|
|
+
|
|
|
+ //删除文件(加密存储完成后删除未加密文件)
|
|
|
+ fs.unlink(localFileDir, 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 = "";
|
|
|
+ if (fileType == "m3u8")
|
|
|
+ {
|
|
|
+ fileName = fileName.replace(".m3u8", "_new.mp4")
|
|
|
+ newFileDir = constObj.baseDir + fileName.replace(/\*/g, '\\');
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ fileName = fileName.replace(".", "_new.")
|
|
|
+ newFileDir = constObj.baseDir + 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)
|
|
|
+ {
|
|
|
+ if (fileUrl.startsWith("https://efunimgs.ai160.com") || fileUrl.startsWith("https://efunvideo.ai160.com"))
|
|
|
+ {
|
|
|
+ /*--文件夹目录-Begin--*/
|
|
|
+ var folderDir = ljConst.constBase.BASE_DIR;
|
|
|
+
|
|
|
+ var splitArr = fileUrl.split('/')
|
|
|
+ var arrLen = splitArr.length;
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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)
|
|
|
+ {
|
|
|
+ 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 + '].');
|
|
|
+ });
|
|
|
+
|
|
|
+ rs.on('error', function (err)
|
|
|
+ {
|
|
|
+ console.log(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 = decryptedContent(c);
|
|
|
+
|
|
|
+ let d = Buffer.from(c, 'hex');
|
|
|
+
|
|
|
+ res.write(d);
|
|
|
+ });
|
|
|
+
|
|
|
+ rs.on('end', function ()
|
|
|
+ {
|
|
|
+ res.end();
|
|
|
+ console.log('Succeeded in reading from decrypted stream[' + fDest + '].');
|
|
|
+ });
|
|
|
+
|
|
|
+ rs.on('error', function (err)
|
|
|
+ {
|
|
|
+ console.log(err.stack);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 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;
|