gulpfile.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  1. /** @format */
  2. const gulp = require('gulp')
  3. const del = require('del')
  4. const shell = require('gulp-shell')
  5. const replace = require('gulp-replace')
  6. const fs = require('fs')
  7. const zip = require('gulp-zip')
  8. const path = require('path')
  9. const Sftp = require('ssh2-sftp-client')
  10. const WebSocket = require('ws')
  11. const https = require('https')
  12. const minimist = require('minimist')
  13. const GulpSSH = require('gulp-ssh')
  14. const vinylPaths = require('vinyl-paths')
  15. const rename = require('gulp-rename')
  16. const ChineseStoryCfg = require('./config/output/ChineseStoryCfg.js')
  17. const ChineseWordCfg = require('./config/output/ChineseWordCfg.js')
  18. const PinyinChange = require('./config/output/PinyinChange.js')
  19. const PoetryQuestionCfg = require('./config/output/PoetryQuestionCfg.js')
  20. // 配置
  21. const knownOptions = {
  22. string: 'version',
  23. string: 'cocosPath',
  24. string: 'buildPath',
  25. string: 'debugBool',
  26. string: 'releaseBool',
  27. string: 'h5RemoteUrl',
  28. string: 'configUrl',
  29. string: 'packageName',
  30. string: 'keystorePath',
  31. string: 'isQQ',
  32. string: 'isTT',
  33. string: 'appid',
  34. string: 'appType',
  35. default: {
  36. version: '1',
  37. cocosPath: 'D:/cocosEditors/Creator/2.4.8/CocosCreator.exe',
  38. buildPath: './build',
  39. debugBool: 'false',
  40. releaseBool: 'false',
  41. h5RemoteUrl: '/www/wwwroot/zedu-cocos-jxb-dev/',
  42. configUrl: '',
  43. packageName: 'com.zedu',
  44. autoCompile: 'false',
  45. keystorePath: 'D:/keystore/key.store',
  46. appid: 'wx7a1fbe2f424a93b4',
  47. isQQ: 'false',
  48. isTT: 'false',
  49. appType: 1,
  50. gameType: 1,
  51. },
  52. }
  53. const options = minimist(process.argv.slice(2), knownOptions)
  54. const resServerConfig = {
  55. host: options.debugBool == 'true' || options.releaseBool == 'true' ? '47.107.63.156' : '47.107.63.156',
  56. port: 22,
  57. username: 'root',
  58. privateKey: fs.readFileSync('D:\\cer\\id_rsa'),
  59. }
  60. let getGulpSSH = (isTest = true) => {
  61. return new GulpSSH({
  62. ignoreErrors: false,
  63. sshConfig: {
  64. host: isTest ? '47.107.63.156' : '47.107.63.156',
  65. port: 22,
  66. username: 'root',
  67. privateKey: fs.readFileSync('D:\\cer\\id_rsa'),
  68. },
  69. })
  70. }
  71. const zipName = 'upload.zip'
  72. const testResUrl = ''
  73. const onlineResUrl = ''
  74. const localUploadUrl = path.join(options.buildPath, 'upload')
  75. // shell
  76. let shellTask = `${options.cocosPath} --path ./ --build "buildPath=${options.buildPath};platform=wechatgame;debug=${options.debugBool};"`
  77. let h5ShellTask = `${options.cocosPath} --path ./ --build "buildPath=${
  78. options.buildPath
  79. };platform=web-mobile;embedWebDebugger=${options.debugBool == 'true' || options.releaseBool == 'true'};debug=${
  80. options.debugBool
  81. };"`
  82. let IOSShellTask = `${options.cocosPath} --path ./ --build "buildPath=${options.buildPath};platform=ios;template=link;debug=${options.debugBool};"`
  83. let AndroidShellTask = `${options.cocosPath} --path ./ --build "buildPath=${options.buildPath};platform=android;template=link;debug=${options.debugBool};autoCompile=${options.autoCompile};useDebugKeystore=false;keystorePath=${options.keystorePath};keystorePassword=888888;keystoreAlias=app-key;keystoreAliasPassword=666666;apiLevel=android-30;appABIs=['arm64-v8a','armeabi-v7a'];packageName=${options.packageName};md5Cache=false;"`
  84. let wxsubshellTask = `${options.cocosPath} --path ./wx_sub --build "buildPath=./build/;platform=wechatgame-subcontext"`
  85. let TTShellTask = `${options.cocosPath} --path ./ --build "buildPath=${options.buildPath};platform=bytedance;debug=${options.debugBool};mainIsRemote=true;"`
  86. let win32ShellTask = `${options.cocosPath} --path ./ --build "buildPath=${options.buildPath};platform=win32;template=link;debug=${options.debugBool};md5Cache=false;"`
  87. // 文件修改
  88. gulp.task('copy:Regular', () =>
  89. gulp
  90. .src('./assets/script/data/ConstValue.ts')
  91. .pipe(replace('DEBUG = true', 'DEBUG = ' + options.debugBool))
  92. .pipe(replace('DEBUG = false', 'DEBUG = ' + options.debugBool))
  93. .pipe(gulp.dest('./assets/script/data')),
  94. )
  95. gulp.task('copy:index', () =>
  96. gulp
  97. .src('./build-templates/web-mobile/index.html')
  98. .pipe(
  99. replace(
  100. '<script src="vconsole.min.js"></script>',
  101. `<script src="${
  102. options.debugBool == 'true' || options.releaseBool == 'true' ? 'vconsole.min.js' : ''
  103. }"></script>`,
  104. ),
  105. )
  106. .pipe(
  107. replace(
  108. '<script src=""></script>',
  109. `<script src="${
  110. options.debugBool == 'true' || options.releaseBool == 'true' ? 'vconsole.min.js' : ''
  111. }"></script>`,
  112. ),
  113. )
  114. .pipe(gulp.dest('./build-templates/web-mobile')),
  115. )
  116. const updateJson = (jsonUrl, key, value, key2) => {
  117. const json = JSON.parse(fs.readFileSync(jsonUrl).toString())
  118. if (key2) {
  119. json[key2][key] = value
  120. } else {
  121. json[key] = value
  122. }
  123. fs.writeFileSync(jsonUrl, JSON.stringify(json, '', ' '))
  124. }
  125. gulp.task('copy:REMOTE_SERVER_ROOT', cb => {
  126. let url = options.debugBool == 'true' ? testResUrl : onlineResUrl
  127. url += options.isQQ == 'true' ? 'card-qq' : 'card-wx'
  128. updateJson('./settings/wechatgame.json', 'REMOTE_SERVER_ROOT', url)
  129. updateJson('./settings/wechatgame.json', 'appid', options.appid)
  130. updateJson('./settings/builder.json', 'REMOTE_SERVER_ROOT', url, 'wechatgame')
  131. updateJson('./settings/builder.json', 'appid', options.appid, 'wechatgame')
  132. cb()
  133. })
  134. // 资源上传
  135. const copy = (src, dst) => fs.writeFileSync(dst, fs.readFileSync(src))
  136. const mkdirsSync = dirname => {
  137. if (fs.existsSync(dirname)) {
  138. return true
  139. } else if (mkdirsSync(path.dirname(dirname))) {
  140. fs.mkdirSync(dirname)
  141. return true
  142. }
  143. return false
  144. }
  145. const delDir = dirPath => {
  146. let files = []
  147. if (fs.existsSync(dirPath)) {
  148. files = fs.readdirSync(dirPath)
  149. files.forEach(file => {
  150. const curPath = path.join(dirPath, file)
  151. if (fs.statSync(curPath).isDirectory()) {
  152. delDir(curPath) // 递归删除文件夹
  153. } else {
  154. fs.unlinkSync(curPath) // 删除文件
  155. }
  156. })
  157. fs.rmdirSync(dirPath)
  158. }
  159. }
  160. const readFileList = (filePath, filesList = []) => {
  161. const files = fs.readdirSync(filePath)
  162. files.forEach(fileName => {
  163. const fullPath = path.join(filePath, fileName)
  164. if (fs.statSync(fullPath).isDirectory()) {
  165. readFileList(fullPath, filesList) // 递归读取文件
  166. } else {
  167. filesList.push(fullPath)
  168. }
  169. })
  170. return filesList
  171. }
  172. const curUpLoadResRecord = []
  173. let UpLoadResRecordKeys = []
  174. const UpLoadResRecordUrl = options.debugBool == 'true' ? './UpLoadResTestRecord.txt' : './UpLoadResRecord.txt'
  175. const uploadRes = (inputPath, platfrom) => {
  176. const files = readFileList(inputPath)
  177. if (!fs.existsSync(UpLoadResRecordUrl)) {
  178. fs.writeFileSync(UpLoadResRecordUrl, '', 'utf-8')
  179. }
  180. UpLoadResRecordKeys = fs.readFileSync(UpLoadResRecordUrl, 'utf-8').split('\r\n')
  181. UpLoadResRecordKeys = UpLoadResRecordKeys.filter(value => value != '')
  182. delDir(localUploadUrl) // 清空打包文件夹
  183. mkdirsSync(localUploadUrl) // 建立打包文件夹
  184. // 分析差异文件,拷贝到打包文件夹
  185. for (let i = 0; i < files.length; i++) {
  186. if (UpLoadResRecordKeys.indexOf(files[i]) < 0 || files[i].indexOf('index.html') != -1) {
  187. const dist = files[i].replace(path.join(options.buildPath, platfrom), localUploadUrl)
  188. // console.log(i, files[i], dist);
  189. mkdirsSync(path.resolve(dist, '..')) // 确保拷贝前目录存在
  190. if (fs.existsSync(files[i])) {
  191. copy(files[i], dist)
  192. } else {
  193. console.log(' uploadRes copy file:', files[i], 'no exist')
  194. }
  195. curUpLoadResRecord.push(files[i])
  196. }
  197. }
  198. mkdirsSync(path.join(localUploadUrl, 'empty'))
  199. // 拷贝分享目录
  200. /*
  201. const share = readFileList('./share');
  202. for (let i = 0; i < share.length; i++) {
  203. const dist = path.join(localUploadUrl, share[i]);
  204. mkdirsSync(path.resolve(dist, '..')); // 确保拷贝前目录存在
  205. copy(share[i], dist);
  206. }
  207. */
  208. }
  209. gulp.task('copy:uploadRes', cb => {
  210. let platform = 'wechatgame'
  211. if (options.isTT == 'true') platform = 'bytedance'
  212. uploadRes(`${options.buildPath}/${platform}/remote/`, platform)
  213. cb()
  214. })
  215. gulp.task('copy:uploadResH5', cb => {
  216. uploadRes(`${options.buildPath}/web-mobile/`, 'web-mobile')
  217. cb()
  218. })
  219. gulp.task('Zip', () => gulp.src(`${localUploadUrl}/**/*`).pipe(zip(zipName)).pipe(gulp.dest(localUploadUrl)))
  220. let preTime = 0
  221. let curByte = 0
  222. const ftpUpload = (cb, remoteUrl) => {
  223. let sftp = new Sftp()
  224. sftp.connect(resServerConfig)
  225. .then(() => {
  226. console.log(
  227. 'sftp连接成功,上传中... ',
  228. zipName,
  229. 'localUploadUrl-->',
  230. path.join(localUploadUrl, zipName),
  231. 'remoteUrl--->',
  232. `${remoteUrl}${zipName}`,
  233. )
  234. return sftp.fastPut(path.join(localUploadUrl, zipName), `${remoteUrl}${zipName}`, {
  235. step: (cur, chunk, total) => {
  236. if (preTime == 0) {
  237. preTime = Date.now()
  238. curByte = cur
  239. }
  240. const stepTime = Date.now() - preTime
  241. if (stepTime > 1000) {
  242. preTime = Date.now()
  243. const stepByte = cur - curByte
  244. console.log(((cur / total) * 100).toFixed(2), '% ', (stepByte / 1024).toFixed(2), 'kb/s')
  245. curByte = cur
  246. }
  247. },
  248. })
  249. })
  250. .then(() => {
  251. console.log('上传完成!')
  252. cb()
  253. })
  254. .catch(err => {
  255. console.log('sftp报错', err)
  256. })
  257. .finally(data => {
  258. console.log('sftpfinally', data)
  259. sftp.end()
  260. })
  261. }
  262. let unZipSucc = true
  263. let getDeleteJsonList = function (path, list) {
  264. let files = fs.readdirSync(path)
  265. files.forEach(item => {
  266. let stat = fs.statSync(path + item)
  267. if (stat.isDirectory()) {
  268. getDeleteJsonList(path + item + '/', list)
  269. } else if (stat.size > 0 * 1024) {
  270. list.push(path + item)
  271. }
  272. })
  273. return list
  274. }
  275. gulp.task('clean:res', cb => {
  276. let platform = 'wechatgame'
  277. if (options.isTT == 'true') platform = 'bytedance'
  278. let list = []
  279. let path = `${options.buildPath}/${platform}/remote/`
  280. getDeleteJsonList(path, list)
  281. console.log(list)
  282. del.sync(list, {force: true, allowEmpty: true})
  283. cb()
  284. })
  285. gulp.task('updateUploadUrl', cb => {
  286. if (unZipSucc) {
  287. let apendStr = ''
  288. for (let i = 0; i < curUpLoadResRecord.length; i++) {
  289. apendStr += `${curUpLoadResRecord[i]}\r\n`
  290. }
  291. fs.appendFileSync(UpLoadResRecordUrl, apendStr, 'utf-8')
  292. } else {
  293. console.log('远程解压缩失败')
  294. }
  295. cb()
  296. })
  297. let getRemoteUrl = () => {
  298. let url = ''
  299. return url
  300. }
  301. gulp.task('SFTP', cb => ftpUpload(cb, getRemoteUrl()))
  302. gulp.task('H5SFTP', cb => ftpUpload(cb, options.h5RemoteUrl))
  303. gulp.task('UnZip', cb => {
  304. cb()
  305. // return getGulpSSH()
  306. // .shell(['cd ' + options.h5RemoteUrl, 'unzip -Co upload.zip -d ./'])
  307. })
  308. gulp.task('uploadQiniu', shell.task(`java -DwebRoot=${localUploadUrl} -DdirPath=${localUploadUrl} -jar qiniu.jar`))
  309. gulp.task('uploadResContinueH5', gulp.series('copy:uploadResH5', 'Zip', 'H5SFTP', 'UnZip', 'updateUploadUrl'))
  310. gulp.task('uploadResContinueH5qiniu', gulp.series('copy:uploadResH5', 'uploadQiniu', 'updateUploadUrl'))
  311. gulp.task('uploadResContinue', gulp.series('copy:uploadRes', 'Zip', 'SFTP', 'UnZip', 'clean:res', 'updateUploadUrl'))
  312. Date.prototype.Format = function (fmt) {
  313. let o = {
  314. 'Y+': this.getFullYear(), // 年
  315. 'M+': this.getMonth() + 1, // 月份
  316. 'd+': this.getDate(), // 日
  317. 'h+': this.getHours(), // 小时
  318. 'm+': this.getMinutes(), // 分
  319. 's+': this.getSeconds(), // 秒
  320. 'q+': Math.floor((this.getMonth() + 3) / 3), // 季度
  321. 'S+': this.getMilliseconds(), // 毫秒
  322. }
  323. if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, `${this.getFullYear()}`.substr(4 - RegExp.$1.length))
  324. for (let k in o)
  325. if (new RegExp(`(${k})`).test(fmt))
  326. fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : `00${o[k]}`.substr(`${o[k]}`.length))
  327. return fmt
  328. }
  329. gulp.task('version', () => {
  330. let filePath = './assets/resources/version.json'
  331. let obj = {}
  332. obj.time = new Date().Format('YYMMddhhmmss')
  333. obj.appType = options.appType
  334. obj.gameType = options.gameType
  335. fs.writeFileSync(filePath, JSON.stringify(obj))
  336. return gulp.src('./build-templates/jsb-link/main.js')
  337. .pipe(replace(/window\._game_res_version = \'\d{12}\'/g, `window._game_res_version = \'${obj.time}\'`))
  338. .pipe(gulp.dest('./build-templates/jsb-link/'))
  339. })
  340. gulp.task(
  341. 'wxsub',
  342. gulp.series(
  343. /*'compile-wxsub','copywxsub'*/ cb => {
  344. cb()
  345. },
  346. ),
  347. )
  348. gulp.task('copywxsub', () => gulp.src('./wx_sub/build/**/*').pipe(gulp.dest(`${options.buildPath}/wechatgame`)))
  349. gulp.task('compile-wxsub', shell.task(wxsubshellTask))
  350. gulp.task(
  351. 'WX',
  352. gulp.series('version', 'copy:Regular', 'copy:REMOTE_SERVER_ROOT', shell.task(shellTask), 'uploadResContinue'),
  353. )
  354. gulp.task('H5', gulp.series('version', 'copy:Regular', 'copy:index', shell.task(h5ShellTask), 'uploadResContinueH5'))
  355. gulp.task(
  356. 'H5qiniu',
  357. gulp.series('version', 'copy:Regular', 'copy:index', shell.task(h5ShellTask), 'uploadResContinueH5qiniu'),
  358. )
  359. gulp.task('IOS', gulp.series('version', 'copy:Regular', shell.task(IOSShellTask)))
  360. gulp.task('Android', gulp.series('version', 'copy:Regular', shell.task(AndroidShellTask)))
  361. gulp.task('TT', gulp.series('version', 'copy:Regular', shell.task(TTShellTask), 'uploadResContinue'))
  362. gulp.task('win32', gulp.series('version', 'copy:Regular', shell.task(win32ShellTask)))
  363. const {URL} = require('url')
  364. const cwd = process.cwd()
  365. const root = cwd
  366. ;(exts = ['.jpg', '.png']), (max = 5200000) // 5MB == 5242848.754299136
  367. const tinypngoptions = {
  368. method: 'POST',
  369. hostname: 'tinypng.com',
  370. path: '/web/shrink',
  371. headers: {
  372. rejectUnauthorized: false,
  373. 'Postman-Token': Date.now(),
  374. 'Cache-Control': 'no-cache',
  375. 'Content-Type': 'application/x-www-form-urlencoded',
  376. 'User-Agent':
  377. 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',
  378. },
  379. }
  380. // 生成随机IP, 赋值给 X-Forwarded-For
  381. function getRandomIP() {
  382. return Array.from(Array(4))
  383. .map(() => parseInt(Math.random() * 255))
  384. .join('.')
  385. }
  386. let allUploadFiles = []
  387. let allSuccessUploadFiles = new Map()
  388. let curAllUploadCount = 0
  389. let maxReq = 500
  390. let fileUploadIDs = new Map()
  391. let tinyPngRecords = new Map()
  392. // 获取文件列表
  393. function fileList(folder) {
  394. let files = fs.readdirSync(folder)
  395. files.forEach(file1 => {
  396. let file = path.join(folder, file1)
  397. let stats = fs.statSync(file)
  398. if (
  399. // 必须是文件,小于5MB,后缀 jpg||png
  400. stats.size <= max &&
  401. stats.isFile() &&
  402. exts.includes(path.extname(file))
  403. ) {
  404. // 通过 X-Forwarded-For 头部伪造客户端IP
  405. console.log('可以压缩:' + file)
  406. allUploadFiles.push(file)
  407. }
  408. if (stats.isDirectory()) fileList(file + '/')
  409. })
  410. }
  411. let crypto = require('crypto')
  412. function MD5(img) {
  413. let buffer = fs.readFileSync(img)
  414. let fsHash = crypto.createHash('md5')
  415. fsHash.update(buffer)
  416. return fsHash.digest('hex')
  417. }
  418. function popUploadFiles(cb) {
  419. for (let i = 0; i < maxReq; i++) {
  420. let file = allUploadFiles.pop()
  421. if (file) {
  422. let hasRecord = false
  423. let recordData = tinyPngRecords.get(file)
  424. if (recordData && recordData == MD5(path.join(cwd, file))) {
  425. hasRecord = true
  426. }
  427. if (hasRecord) {
  428. allSuccessUploadFiles.set(file, true)
  429. if (allSuccessUploadFiles.size == curAllUploadCount) {
  430. cb()
  431. } else {
  432. let curCount = allSuccessUploadFiles.size
  433. if (curCount % maxReq == 0) popUploadFiles(cb)
  434. console.log('当前总进度:', allSuccessUploadFiles.size, '/', curAllUploadCount)
  435. }
  436. } else {
  437. fileUpload(file, cb)
  438. }
  439. }
  440. }
  441. }
  442. // 过滤文件格式,返回所有jpg,png图片
  443. function fileFilter(file) {
  444. // fs.stat(file, (err, stats) => {
  445. // if (err) return console.error(err);
  446. //
  447. // });
  448. }
  449. // 异步API,压缩图片
  450. // {"error":"Bad request","message":"Request is invalid"}
  451. // {"input": { "size": 887, "type": "image/png" },"output": { "size": 785, "type": "image/png", "width": 81, "height": 81, "ratio": 0.885, "url": "https://tinypng.com/web/output/7aztz90nq5p9545zch8gjzqg5ubdatd6" }}
  452. function fileUpload(img, cb) {
  453. tinypngoptions.headers['X-Forwarded-For'] = getRandomIP()
  454. let fileUploadID = fileUploadIDs.get(img)
  455. if (fileUploadID) clearTimeout(fileUploadID)
  456. var req = https.request(tinypngoptions, function (res) {
  457. res.on('data', buf => {
  458. try {
  459. let obj = JSON.parse(buf.toString())
  460. if (obj.error) {
  461. console.log(`[${img}]:压缩失败!报错:${obj.message}`)
  462. }
  463. fileUpdate(img, obj, cb)
  464. } catch (e) {
  465. }
  466. })
  467. })
  468. req.write(fs.readFileSync(img), 'binary')
  469. req.on('error', e => {
  470. console.error('fileUpload error:', e)
  471. })
  472. req.end()
  473. fileUploadID = setTimeout(() => {
  474. req.destroy()
  475. fileUpload(img, cb)
  476. }, 10000)
  477. fileUploadIDs.set(img, fileUploadID)
  478. }
  479. // 该方法被循环调用,请求图片数据
  480. function fileUpdate(imgpath, obj, cb, index) {
  481. const outputDir = path.join(cwd, '')
  482. let img = imgpath
  483. imgpath = path.join(cwd, '', imgpath.replace(cwd, ''))
  484. if (!fs.existsSync(outputDir)) {
  485. fs.mkdirSync(outputDir)
  486. }
  487. let options = new URL(obj.output.url)
  488. let req = https.request(options, res => {
  489. let body = ''
  490. res.setEncoding('binary')
  491. res.on('data', function (data) {
  492. body += data
  493. })
  494. res.on('end', function () {
  495. fs.writeFile(imgpath, body, 'binary', err => {
  496. if (err) console.error(err)
  497. console.log(
  498. `[${imgpath}] \n 压缩成功,原始大小-${obj.input.size},压缩大小-${obj.output.size},优化比例-${obj.output.ratio}`,
  499. )
  500. // 替换文件md5或者新增
  501. let recordData = tinyPngRecords.get(img)
  502. let fileMd5 = MD5(img)
  503. if (recordData) {
  504. let content = fs.readFileSync(tinyPngRecordUrl, 'utf-8')
  505. content.replace(img + '||' + recordData, img + '||' + fileMd5)
  506. fs.writeFileSync(tinyPngRecordUrl, content, 'utf-8')
  507. } else {
  508. let str = '\r\n' + img + '||' + fileMd5
  509. fs.appendFileSync(tinyPngRecordUrl, str, 'utf-8')
  510. }
  511. let fileUploadID = fileUploadIDs.get(img)
  512. if (fileUploadID) clearTimeout(fileUploadID)
  513. allSuccessUploadFiles.set(img, true)
  514. if (allSuccessUploadFiles.size == curAllUploadCount) {
  515. cb()
  516. } else {
  517. let curCount = allSuccessUploadFiles.size
  518. if (curCount % maxReq == 0) popUploadFiles(cb)
  519. console.log('当前总进度:', allSuccessUploadFiles.size, '/', curAllUploadCount)
  520. }
  521. })
  522. })
  523. })
  524. req.on('error', e => {
  525. console.error(e)
  526. })
  527. req.end()
  528. }
  529. let tinyPngRecordUrl = './tinyPngRecordUrl.txt'
  530. gulp.task('tinypng', function (cb) {
  531. if (!fs.existsSync(tinyPngRecordUrl)) {
  532. fs.writeFileSync(tinyPngRecordUrl, '', 'utf-8')
  533. }
  534. let tinyPngRecordArr = fs.readFileSync(tinyPngRecordUrl, 'utf-8').split('\r\n')
  535. for (let i = 0; i < tinyPngRecordArr.length; i++) {
  536. let obj = tinyPngRecordArr[i].split('||')
  537. tinyPngRecords.set(obj[0], obj[1])
  538. }
  539. fileList('./assets')
  540. curAllUploadCount = allUploadFiles.length
  541. console.log('tinypng curAllUploadCount:', curAllUploadCount)
  542. popUploadFiles(cb)
  543. })
  544. let semeArr = ['S231', 'S232', 'S341', 'S342', 'S451', 'S452', 'S561', 'S562']
  545. let curG = 'D:/zedu_jxb/assets/gameChildSeme'
  546. gulp.task('copyPng', cb => {
  547. semeArr.forEach(value => {
  548. for (let i = 1; i < 17; i++) {
  549. let g = i.toString().padStart(2, '0')
  550. fs.writeFileSync(
  551. `${curG}/${value}/G${g}/bg.png`,
  552. fs.readFileSync(`D:/zedu_home/gameRes/gameHomeSeme/${value}/G${g}/P01/bg.png`),
  553. )
  554. }
  555. })
  556. cb()
  557. })
  558. gulp.task('deletePng', () => {
  559. return gulp.src([`${curG}/S***/G**/P**/bg.png`, `${curG}/S***/G**/P**/bg.png.meta`]).pipe(vinylPaths(del))
  560. })
  561. gulp.task('deletePart', () => {
  562. return gulp.src([`${curG}/S***/G**/P**/*`]).pipe(vinylPaths(del))
  563. })
  564. gulp.task('deletePrefab', () => {
  565. return gulp.src([`${curG}/S***/G**/**.prefab`, `${curG}/S***/G**/**.prefab.meta`]).pipe(vinylPaths(del))
  566. })
  567. gulp.task('deleteDataJson', () => {
  568. return gulp.src([`${curG}/S***/G**/P**/data.json`, `${curG}/S***/G**/P**/data.json.meta`]).pipe(vinylPaths(del))
  569. })
  570. gulp.task('modifyDataJson', cb => {
  571. for (let value of semeArr) {
  572. for (let i = 1; i <= 16; i++) {
  573. for (let j = 1; j <= 16; j++) {
  574. let path = `${curG}/${value}/G${i.toString().padStart(2, '0')}/P${j
  575. .toString()
  576. .padStart(2, '0')}/data_${value.replace('S', '')}_${i}_${j}.json`
  577. if (fs.existsSync(path)) {
  578. let obj = JSON.parse(fs.readFileSync(path, 'utf-8'))
  579. if (obj.gameMode == 0) {
  580. obj.gameMode = 6
  581. fs.writeFileSync(path, JSON.stringify(obj, null, 2))
  582. }
  583. }
  584. }
  585. }
  586. }
  587. cb()
  588. })
  589. gulp.task('copyMp3', () => {
  590. return gulp.src([`D:/zedu_jxb/gameRes/gameChildSeme/**/**~1.mp3`]).pipe(rename(function (path) {
  591. // Updates the object in-place
  592. path.basename = path.basename.replace("~1", "")
  593. })).pipe(gulp.dest('D:/zedu_jxb/gameRes/gameChildSeme/'))
  594. })
  595. let qiniu = require("qiniu");
  596. let qiniuJS = require("qiniu-js");
  597. var accessKey = '0Alb-OTi_yKnSAa71Ey_4vPtNVlGJOMWUrYMh_ql';
  598. var secretKey = '28QoQoyhUSIO8zhZnSOwtJeBXSDcCWp_0f72tSnL';
  599. var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
  600. function uptoken(bucket, keyToOverwrite) {
  601. var options = {
  602. scope: bucket + ":" + keyToOverwrite,
  603. expires: 7200
  604. }
  605. var putPolicy = new qiniu.rs.PutPolicy(options);
  606. var uploadToken = putPolicy.uploadToken(mac);
  607. return uploadToken;
  608. }
  609. //构造上传函数
  610. function qiniuUploadFile(localFile) {
  611. let key = localFile.replace('D:/zedu_jxb/packages-hot-update/manifest/', '')
  612. if (localFile.includes('build/jsb-link')) {
  613. key = localFile.replace('D:/zedu_jxb/build/jsb-link/', '')
  614. }
  615. key = 'hotupdate/' + key
  616. //生成上传 Token
  617. token = uptoken('zedu-cdn', key);
  618. let config = new qiniu.conf.Config()
  619. config.zone = qiniu.zone.Zone_z2
  620. var formUploader = new qiniu.form_up.FormUploader(config);
  621. // 文件上传
  622. formUploader.putFile(token, key, localFile, new qiniu.form_up.PutExtra(), function (respErr,
  623. respBody, respInfo) {
  624. if (respErr) {
  625. throw respErr;
  626. }
  627. if (respInfo.statusCode == 200) {
  628. console.log('七牛上传成功:-------------》', respBody);
  629. } else {
  630. qiniuUploadFile(localFile)
  631. console.log('七牛上传失败:-------------》', respInfo);
  632. }
  633. });
  634. }
  635. let bucketPathName = ['', '', '', '', '', '', 'chinese', 'math', 'poetry', 'pinyin']
  636. bucketPathName[100] = 'win32'
  637. let upLoadHotAssets = {}
  638. const UpLoadHotUrl = './UpLoadHotResRecord.txt'
  639. const uploadHotRes = () => {
  640. let projectManifestUrl = `D:/zedu_jxb/packages-hot-update/${bucketPathName[options.gameType]}/manifest/project.manifest`
  641. let versionManifestUrl = `D:/zedu_jxb/packages-hot-update/${bucketPathName[options.gameType]}/manifest/version.manifest`
  642. let files = []
  643. readFileList('D:/zedu_jxb/build/jsb-link/assets', files)
  644. readFileList('D:/zedu_jxb/build/jsb-link/src', files)
  645. if (!fs.existsSync(UpLoadHotUrl)) {
  646. fs.writeFileSync(UpLoadHotUrl, JSON.stringify({}), 'utf-8')
  647. }
  648. let curAssets = JSON.parse(fs.readFileSync(projectManifestUrl, 'utf-8')).assets
  649. upLoadHotAssets = JSON.parse(fs.readFileSync(UpLoadHotUrl, 'utf-8'))
  650. delDir(localUploadUrl) // 清空打包文件夹
  651. mkdirsSync(localUploadUrl) // 建立打包文件夹
  652. copy(projectManifestUrl, localUploadUrl + '\\project.manifest')
  653. copy(versionManifestUrl, localUploadUrl + '\\version.manifest')
  654. // 分析差异文件,拷贝到打包文件夹
  655. for (let i = 0; i < files.length; i++) {
  656. let file = files[i]
  657. let srcKey = file.replace('D:\\zedu_jxb\\build\\jsb-link\\', '').replace(new RegExp('\\\\', 'g'), '/')
  658. let srcObj = upLoadHotAssets[srcKey]
  659. if (!srcObj || curAssets[srcKey].md5 != srcObj.md5) {
  660. const dist = file.replace('D:\\zedu_jxb\\build\\jsb-link', localUploadUrl)
  661. // console.log(i, files[i], dist);
  662. mkdirsSync(path.resolve(dist, '..')) // 确保拷贝前目录存在
  663. if (fs.existsSync(file)) {
  664. copy(file, dist)
  665. upLoadHotAssets[srcKey] = srcObj
  666. } else {
  667. console.log(' uploadRes copy file:', file, 'no exist')
  668. }
  669. }
  670. }
  671. }
  672. gulp.task('updateHotUploadTxt', cb => {
  673. fs.writeFileSync(UpLoadHotUrl, JSON.stringify(upLoadHotAssets), 'utf-8')
  674. cb()
  675. })
  676. gulp.task('QiniuHotFresh', (cb) => {
  677. var dirsToRefresh = [
  678. 'https://hotupdate.zhi-edu.com/'
  679. ];
  680. var cdnManager = new qiniu.cdn.CdnManager(mac);
  681. //单次请求链接不可以超过10个,如果超过,请分批发送请求
  682. cdnManager.refreshDirs(dirsToRefresh, function (err, respBody, respInfo) {
  683. if (err) {
  684. throw err;
  685. }
  686. console.log(respInfo.statusCode);
  687. if (respInfo.statusCode == 200) {
  688. console.log(dirsToRefresh, '七牛cdn刷新成功');
  689. }
  690. });
  691. cb()
  692. })
  693. gulp.task('uploadQiniuHot', shell.task(`java -DwebRoot=${localUploadUrl} -DdirPath=${localUploadUrl} -Dbucket=zedu-hotupdate -DbucketPath=${bucketPathName[options.gameType]} -jar qiniu.jar`))
  694. gulp.task('copyUploadHot', (cb) => {
  695. uploadHotRes()
  696. cb()
  697. })
  698. gulp.task('uploadHotUpdate', gulp.series('copyUploadHot', 'uploadQiniuHot', 'updateHotUploadTxt', 'QiniuHotFresh'))
  699. let semeWordPinyin = {}
  700. let semeStroke = {}
  701. const {pinyin} = require('pinyin-pro');
  702. var cnchar = require('cnchar');
  703. var cncharorder = require('cnchar-order');
  704. let getTsFileString = (jsobj, fileName) => {
  705. var s = `export let ${fileName}=\r\n{`
  706. for (let key in jsobj) {
  707. s += `\r\n '${key}':{ `
  708. for (let key1 in jsobj[key]) {
  709. s += `${key1}: '${jsobj[key][key1]}', `
  710. }
  711. s = s.substr(0, s.length - 2)
  712. s += ' },'
  713. }
  714. s = s.substr(0, s.length - 1) // 单个对象为表去掉多余的数组逗号
  715. s += '\r\n}'
  716. return s
  717. }
  718. gulp.task('getpinyin', (cb) => {
  719. let newCfg = {}
  720. cnchar.use(cncharorder)
  721. for (let key in ChineseWordCfg) {
  722. let wordCfg = ChineseWordCfg[key]
  723. let word = wordCfg.word
  724. let strokeArr = cnchar.stroke(word, 'order', 'detail')[0]
  725. let tempArr = []
  726. strokeArr.forEach((item) => {
  727. if (item.name == '点2') {
  728. item.shape = '丶'
  729. }
  730. tempArr.push(item.shape.split('|')[0])
  731. })
  732. let obj = {}
  733. obj.id = wordCfg.id
  734. obj.word = word
  735. obj.phrase1 = wordCfg.phrase1
  736. obj.phrase2 = wordCfg.phrase2
  737. obj.pinyin = pinyin(word)
  738. obj.stroke = tempArr.join('、')
  739. let pinyinchange = PinyinChange[wordCfg.id]
  740. if (pinyinchange) {
  741. obj.pinyin = pinyinchange.newpy
  742. }
  743. newCfg[key] = obj
  744. }
  745. fs.writeFileSync('./assets/script/config/ChineseWordCfg.ts', getTsFileString(newCfg, 'ChineseWordCfg'), 'utf-8')
  746. cb()
  747. })
  748. gulp.task('getstorypinyin', (cb) => {
  749. let newCfg = {}
  750. cnchar.use(cncharorder)
  751. let semeArr = [231, 232, 341, 342, 451, 452, 561, 562]
  752. for (let i = 0; i < semeArr.length; i++) {
  753. for (let j = 1; j < 17; j++) {
  754. let key = semeArr[i] + '_' + j
  755. let oldCfg = {}
  756. if (ChineseStoryCfg[key]) oldCfg = ChineseStoryCfg[key]
  757. let obj = {}
  758. obj.title = oldCfg.title == undefined ? '' : oldCfg.title
  759. obj.tip = oldCfg.tip == undefined ? '' : oldCfg.tip
  760. obj.titlePinyin = (oldCfg.titlePinyin == undefined || oldCfg.titlePinyin == 0) ? pinyin(obj.title) : oldCfg.titlePinyin
  761. obj.story = oldCfg.story == undefined ? '' : oldCfg.story
  762. // obj.storyPinyin = ''
  763. // for (let i = 0; i < obj.story.length; i++) {
  764. // obj.storyPinyin += pinyin(obj.story.charAt(i))
  765. // }
  766. obj.storyPinyin = (oldCfg.storyPinyin == undefined || oldCfg.storyPinyin == 0) ? pinyin(obj.story) : oldCfg.storyPinyin
  767. obj.story = obj.story.replace(new RegExp('\n', 'g'), '\\n').replace(new RegExp('\t', 'g'), '\\t').replace(new RegExp('\b', 'g'), '\\b')
  768. obj.storyPinyin = obj.storyPinyin.replace(new RegExp('\n', 'g'), '\\n').replace(new RegExp('\t', 'g'), '\\t').replace(new RegExp('\b', 'g'), '\\b')
  769. let pinyinchange = PinyinChange[key]
  770. // 取消配置修改
  771. if (pinyinchange && false) {
  772. let oldArr = pinyinchange.oldpy.split('|')
  773. let newArr = pinyinchange.newpy.split('|')
  774. for (let k = 0; k < oldArr.length; k++) {
  775. if (newArr[k]) {
  776. obj.storyPinyin = obj.storyPinyin.replace(new RegExp(oldArr[k], 'g'), newArr[k])
  777. obj.titlePinyin = obj.titlePinyin.replace(new RegExp(oldArr[k], 'g'), newArr[k])
  778. }
  779. }
  780. }
  781. obj.isCenter = oldCfg.isCenter
  782. newCfg[key] = obj
  783. }
  784. }
  785. fs.writeFileSync('./assets/script/config/ChineseStoryCfg.ts', getTsFileString(newCfg, 'ChineseStoryCfg'), 'utf-8')
  786. cb()
  787. })
  788. gulp.task('copyWrite', (cb) => {
  789. cnchar.use(cncharorder)
  790. for (let key in ChineseWordCfg) {
  791. let value = ChineseWordCfg[key].word
  792. copy('D:/cocosProject/SSRSVGWriterComponent/assets/resources/hanzi-writer-data/' + value + '.json', './assets/gameChineseSeme/writedata/' + value + '.json')
  793. }
  794. cb()
  795. })
  796. gulp.task('getpoetry', (cb) => {
  797. let cfg = {}
  798. for (let key in PoetryQuestionCfg) {
  799. cfg[key] = {}
  800. Object.assign(cfg[key], PoetryQuestionCfg[key])
  801. cfg[key].poetry = cfg[key].poetry.replace(/\n/g, '\\n')
  802. cfg[key].poetryPinyin = cfg[key].poetryPinyin.replace(/\n /g, '\\n')
  803. }
  804. fs.writeFileSync('./assets/script/config/PoetryQuestionCfg.ts', getTsFileString(cfg, 'PoetryQuestionCfg'), {encoding: 'utf8'})
  805. cb()
  806. })
  807. gulp.task('normalCfg', (cb) => {
  808. let cfgName = ['PoetryStoryCfg']
  809. for (let name of cfgName) {
  810. let curJs = require(`./config/output/${name}.js`)
  811. if (!curJs) curJs = {}
  812. let cfg = {}
  813. for (let key in curJs) {
  814. cfg[key] = {}
  815. Object.assign(cfg[key], curJs[key])
  816. }
  817. fs.writeFileSync(`./assets/script/config/${name}.ts`, getTsFileString(cfg, name), {encoding: 'utf8'})
  818. }
  819. cb()
  820. })
  821. gulp.task('excle2js', shell.task('excel2x -i ./config -o ./config/output -t js_alone'))
  822. gulp.task('updateConfig', gulp.series('excle2js', 'getpinyin', 'getstorypinyin', 'getpoetry', 'normalCfg'))