Files
ZYZ/gm-server/app/controller/upload.ts
2023-04-03 17:59:06 +08:00

337 lines
12 KiB
TypeScript

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';
import { decodeArrayStr } from '@pubUtils/util';
import { getLocalQrCodeUrl, getRemoteQrCodeUrl } from '@pubUtils/battleUtils';
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(null);
});
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;
let writeStream;
while ((stream = await parts()) != null) {
console.log('******', stream);
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);
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);
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, 'dicParam.js');
let target2 = path.join(this.config.baseDir, this.distTsFolder, filename);
writeStream = fs.createWriteStream(target1);
await pump(stream, writeStream);
fs.copyFileSync(target1, target2);
files.push(filename);
}
let envs = decodeArrayStr(ctx.request.headers.env||'', ',');
if(envs.length > 0) { // 转发
for(let env of envs) {
if(env == ctx.app.config.realEnv) continue;
await ctx.service.utils.transmit(env, ctx.request.url, writeStream);
}
}
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;
}
}
public async uploadQrCode() {
const { ctx } = this;
try {
const { ctx } = this;
const parts = ctx.multipart();
let part, env = ctx.request.headers.env, fileName = '';
while ((part = await parts()) != null) {
if (part.length) {
console.log('kv: ', `${part[0]}: ${part[1]}`);
} else {
if (!part.filename) continue;
let filenames = part.filename.split('.');
let ext = filenames[filenames.length - 1];
let writePath = getLocalQrCodeUrl(this.app.config.realEnv, env);
fileName = `QR${moment().valueOf()}.${ext}`;
let fullPath = `${writePath}/${fileName}`;
try {
fs.accessSync(writePath);
} catch (err) {
if (err) {
fs.mkdirSync(writePath, { recursive: true });
}
}
if (!fs.existsSync(fullPath)) {
fs.writeFileSync(fullPath, '');
}
const writeStream = fs.createWriteStream(fullPath);
await pump(part, writeStream);
// remoteUrl = `${getRemoteRplUrl(ctx.app.config.realEnv, roleId, warType, battleCode)}/${battleCode}.bin`;
}
await sendToWormhole(part);
}
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: true, url: getRemoteQrCodeUrl(env, fileName) });
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);
});
})
}