import { Controller } from 'egg'; import { Stream } from 'stream'; import { STATUS } from '@consts'; const fs = require('fs'); const path = require('path'); const unzip = require("unzip-stream"); const temp = require('temp'); const compressing = require("compressing"); const moment = require("moment"); const pump = require('mz-modules/pump'); const folderName = 'hot_update_files'; const hotUpdateAddr = `/root/${folderName}`; const publishPath = '/root/hot_update_backup'; import {exec} from 'child_process' import { reloadResources } from '@pubUtils/data'; const sendToWormhole = require('stream-wormhole'); export default class UploadController extends Controller { private deleteFolder (path) { let files = []; if( fs.existsSync(path) ) { files = fs.readdirSync(path); files.forEach((file) =>{ let curPath = path + "/" + file; if(fs.statSync(curPath).isDirectory()) { this.deleteFolder(curPath); } else { fs.unlinkSync(curPath); } }); fs.rmdirSync(path); } } private getFileList (p: string, resultArr: Array<{path:string,name:string}>) { console.log(p, fs.existsSync(p)) if( fs.existsSync(p) ) { let files = fs.readdirSync(p); console.log(p, files, resultArr); files.forEach((file) =>{ let curPath = p + "/" + file; // console.log(curPath, fs.statSync(curPath).isDirectory()) if(fs.statSync(curPath).isDirectory()) { this.getFileList(curPath, resultArr); } else { resultArr.push({ path: curPath, name: file }); } }); } } public async upload() { const { ctx } = this; const stream = await ctx.getFileStream(); // const filename = stream.filename; // const target = path.join(url, filename); // const writeStream = fs.createWriteStream(target); if(stream.mimeType == 'application/zip') { try { let dirPath = await temp.mkdir(folderName); // 临时文件夹 // 解压上传文件的stream await this.doUnzipExtra(stream, dirPath); console.log('读取文件'); let files:any = fs.readdirSync(dirPath); let msg = '', result = ''; for(let file of files) { let flag = false; let arr = ['project.manifest', 'version.manifest', 'assets', 'src']; for(let fileName of arr) { console.log(`${dirPath}/${file}/${fileName}`); let result = fs.existsSync(`${dirPath}/${file}/${fileName}`); if(!result) { msg = '缺少文件' + fileName; flag = true; break; } } if(flag) { continue; } else { result = file; dirPath += `/${file}`; }; } if(!result) { throw new Error(msg); } // 历史记录压缩移动 console.log('历史记录压缩移动'); let isEmpty = true; try { let oldFiles:any = fs.readdirSync(hotUpdateAddr); console.log(oldFiles.length); isEmpty = oldFiles.length <= 0; } catch(e) { isEmpty = true; } if(!isEmpty) { await compressing.zip.compressDir(hotUpdateAddr, `${dirPath}/${folderName}.zip`); if (!fs.existsSync(publishPath)) { fs.mkdirSync(publishPath); } let versionManifest: string = ''; try { let version = JSON.parse(fs.readFileSync(`${hotUpdateAddr}/version.manifest`)); versionManifest = version.version; }catch(e) { console.log(e); } fs.renameSync(`${dirPath}/${folderName}.zip`, `${publishPath}/${folderName}_${versionManifest}_${moment().format('YYMMDDHHmmss')}.zip`); // 删除原始文件 this.deleteFolder(hotUpdateAddr); } // 保存新文件 console.log('保存至热更新地址'); fs.renameSync(dirPath, hotUpdateAddr); temp.cleanupSync(); return ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS); } catch (err) { console.log(err) return ctx.body = ctx.service.utils.resResult(STATUS.GM_UPLOAD_FORMAT_ERR, null, err.message); } } else { return ctx.body = ctx.service.utils.resResult(STATUS.GM_UPLOAD_FORMAT_ERR); } } private async doUnzipExtra(stream: Stream, dirPath: string) { return new Promise((resolve, reject) => { // 解压上传文件的stream var unzipExtractor = unzip.Extract({ path: dirPath }); unzipExtractor.on('close', function() { resolve(); }); unzipExtractor.on('close', function(e) { reject(e); }); stream.pipe(unzipExtractor); // 异步写入文件 }) } private jsonFolder = '/app/resource/jsons'; private warjsonFolder = '/app/resource/warJsons'; private tsFolder = '/app/pubUtils'; private distJsonFolder = `/../game-server/dist${this.jsonFolder}`; private distWarjsonFolder = `/../game-server/dist${this.warjsonFolder}`; private distTsFolder = `/../game-server/dist${this.tsFolder}`; public async uploadJson() { const { ctx } = this; const parts = ctx.multipart({ }); const files = []; try { let stream; while ((stream = await parts()) != null) { const filename = stream.filename; let filenameWithoutEx = filename?filename.split('.')[0]:''; if (stream.mimeType == 'application/json') { if (filenameWithoutEx.match(/^\d{1,}$/)) { let target1 = path.join(this.config.baseDir, this.warjsonFolder, filename); let target2 = path.join(this.config.baseDir, this.distWarjsonFolder, filename); const writeStream = fs.createWriteStream(target1); await pump(stream, writeStream); fs.copyFileSync(target1, target2); files.push(filename); } else { let target1 = path.join(this.config.baseDir, this.jsonFolder, filename); let target2 = path.join(this.config.baseDir, this.distJsonFolder, filename); const writeStream = fs.createWriteStream(target1); await pump(stream, writeStream); fs.copyFileSync(target1, target2); files.push(filename); } } else if (stream.mimeType == 'application/octet-stream') { let target1 = path.join(this.config.baseDir, this.tsFolder, filename); let target2 = path.join(this.config.baseDir, this.distTsFolder, filename); const writeStream = fs.createWriteStream(target1); await pump(stream, writeStream); fs.copyFileSync(target1, target2); files.push(filename); } sendToWormhole(stream); } }catch(e) { console.error(e); } return ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { "files": files }); // console.log('****', stream); // // const filename = stream.filename; // // const target = path.join(url, filename); // if(stream.mimeType == 'application/zip') { // try { // let dirPath = await temp.mkdir(this.jsonFolderName); // 临时文件夹 // console.log('读取文件'); // await this.doUnzipExtra(stream, dirPath); // let files = new Array<{path:string,name:string}>(); // this.getFileList(dirPath, files); // console.log(files); // console.log('保存文件'); // if (!fs.existsSync(this.jsonFolderPath)) { // fs.mkdirSync(this.jsonFolderPath); // } // for (let {path, name} of files) { // console.log(`${this.jsonFolderPath}/${name}`); // fs.renameSync(path, `${this.jsonFolderPath}/${name}`); // } // temp.cleanupSync(); // return ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { // "files": JSON.stringify(files) // }); // } catch (err) { // console.log(err) // return ctx.body = ctx.service.utils.resResult(STATUS.INTERNAL_ERR); // } // } else { // return ctx.body = ctx.service.utils.resResult(STATUS.GM_UPLOAD_FORMAT_ERR); // } } public async reloadResource() { const { ctx } = this; try { reloadResources(); ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: true }); return; } catch(e) { ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: false, err: e.stack }); return; } } } export function childExec(commond: string) { return new Promise((resolve, reject) => { exec(commond, function(error, stdout, stderr){ if(error) { reject(error); return; } console.log('stdout: ' + stdout); console.log('stderr: ' + typeof stderr); resolve(stdout); }); }) }