downloadController.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. let OSS = require('ali-oss');
  2. let fs = require('fs');
  3. let path = require('path');
  4. var archiver = require('archiver');
  5. let request = require("request");
  6. let http = require('http');
  7. let compressPercent = 0;
  8. let taskCodeQueue = [];
  9. let taskDownloaderQueue = {};
  10. let pathConfig = {
  11. ossPrefix: 'http://4dkankan.oss-cn-shenzhen.aliyuncs.com/', // oss前缀
  12. // serverPrefix: 'http://192.168.0.61:8888/', // 服务器前缀
  13. serverPrefix: 'https://test.4dkankan.com/back/down/server/controller/', // 服务器前缀
  14. localDataName: 'localData',
  15. layer: './', // 层级
  16. rootFold: 'tmpData', // 根目录文件夹
  17. staticPath: [ // 静态数据路径
  18. 'static'
  19. ],
  20. paths: [ // images/data路径
  21. 'images/images',
  22. 'data/data'
  23. ],
  24. filters: ['tiles'],
  25. dynamicPath: [ // 一些使用api加载的数据, 提前写入json中
  26. 'http://pro.4dkankan.com/api/scene/getInfo'
  27. ],
  28. }
  29. function downloader() {
  30. this.objArr = [];
  31. this.completeFolds = 0; // 记录已经递归完成的目录, completeFolds == foldNum, 表示所有目录递归完成
  32. this.fileNum = 0; // 需要下载的文件总数
  33. this.completeFileNum = 0; // 已经下载完成的文件总数
  34. this.threadNum = 10; // 同时并发的线程总数
  35. this.curActiveThread = 0;
  36. this.sta = 0;
  37. this.downloadProcess = 0; // 下载进度
  38. this.zipProcess = 0; // 压缩进度
  39. this.sceneCode = ''; // 场景码
  40. this.sceneInfo = ''; // 场景的其他json数据
  41. this.downloadResponse = null;
  42. this.timer = null; // 定时器
  43. this.dirPath = path.join(__dirname, pathConfig.rootFold);
  44. this.foldNum = pathConfig.paths.length + pathConfig.staticPath.length; // 记录目录的总数, 用于判断是否目录是否递归完成
  45. this.client = new OSS({ // 配置阿里云oss
  46. region: 'oss-cn-shenzhen',
  47. accessKeyId: 'LTAIUrvuHqj8pvry',
  48. accessKeySecret: 'JLOVl0k8Ke0aaM8nLMMiUAZ3EiiqI4',
  49. bucket: '4dkankan',
  50. })
  51. }
  52. downloader.prototype.createRootFold = function (data) {
  53. //console.log('pathpath' + this.dirPath);
  54. if (!fs.existsSync(this.dirPath)) {
  55. fs.mkdirSync(this.dirPath);
  56. } else {
  57. console.log('文件夹已存在');
  58. }
  59. this.dirPath = path.join(this.dirPath, '/' + data);
  60. // 创建文件夹目录
  61. if (!fs.existsSync(this.dirPath)) {
  62. fs.mkdirSync(this.dirPath);
  63. } else {
  64. console.log('文件夹已存在');
  65. }
  66. // this.dirPath = path.join(this.dirPath, '/' + 'static');
  67. // // 创建文件夹目录
  68. // if (!fs.existsSync(this.dirPath)) {
  69. // fs.mkdirSync(this.dirPath);
  70. // } else {
  71. // console.log('文件夹已存在');
  72. // }
  73. }
  74. downloader.prototype.listDir = async function (dir, prefixDir) {
  75. let that = this;
  76. for (let i = 0; i < pathConfig.filters.length; i++) {
  77. let filter = pathConfig.filters[i];
  78. if (dir.indexOf(filter) > 0) {
  79. console.log('过滤的路径' + dir);
  80. that.completeFolds++;
  81. if (that.completeFolds === that.foldNum) {
  82. that.fileNum = that.objArr.length;
  83. that.timer = setInterval(that.download, 16);
  84. }
  85. return;
  86. }
  87. }
  88. let result = await this.client.list({
  89. prefix: dir,
  90. delimiter: '/',
  91. 'max-keys': 1000 // 最大限制1000
  92. });
  93. // let dirArr = [];
  94. // if(staticSta){
  95. // dirArr = `static/${dir}`.split('/');
  96. // }else{
  97. // }
  98. dirArr = `${prefixDir}${dir}`.split('/');
  99. let tmpDir = '';
  100. let childDirPath;
  101. dirArr.forEach((item) => {
  102. tmpDir += '/' + item;
  103. childDirPath = path.join(this.dirPath, '/' + tmpDir);
  104. if (!fs.existsSync(childDirPath)) {
  105. fs.mkdirSync(childDirPath);
  106. console.log('创建文件夹成功: ' + childDirPath);
  107. } else {
  108. console.log('文件夹已存在');
  109. }
  110. });
  111. result.objects && result.objects.forEach(async function (obj) {
  112. let downloadURL = pathConfig.ossPrefix + obj.name;
  113. let arr = obj.name.split('/');
  114. let objName = arr[arr.length - 1];
  115. if (objName === '') {
  116. return; // 过滤非法文件, 因为阿里云有时候会把文件夹当作文件返回,
  117. }
  118. // if(obj.name.indexOf('images') >-1){
  119. // console.log(obj);
  120. // }
  121. // let writeURL = childDirPath + '/' + objName;
  122. let writeURL = path.join(childDirPath, objName);
  123. let tack = {
  124. downloadURL: downloadURL,
  125. writeURL: writeURL
  126. }
  127. that.objArr.push(tack);
  128. });
  129. if (result.prefixes) { // 如果当前目录还有子目录存在, 则继续递归
  130. this.foldNum--; // 移除当前目录
  131. let that = this;
  132. result.prefixes.forEach(function (subDir) {
  133. that.foldNum++; // 记录当前目录的子目录
  134. that.listDir(subDir, prefixDir);
  135. });
  136. } else { // 当前目录已经递归完成
  137. this.completeFolds++;
  138. console.log(this.completeFolds + ' ' + this.foldNum)
  139. if (this.completeFolds === this.foldNum) {
  140. // console.log(this.objArr)
  141. this.fileNum = this.objArr.length;
  142. this.timer = setInterval(this.download.bind(this), 16);
  143. }
  144. }
  145. }
  146. downloader.prototype.download = function () {
  147. if (this.objArr.length === 0) {
  148. clearInterval(this.timer);
  149. this.timer = null;
  150. return;
  151. }
  152. if (this.curActiveThread <= this.threadNum) {
  153. let task = this.objArr.shift();
  154. let stream = fs.createWriteStream(task.writeURL);
  155. let readStream = request(task.downloadURL).on('error', function () {
  156. console.log("文件[" + task.downloadURL + "]下载出错 ");
  157. }).pipe(stream);
  158. let that = this;
  159. readStream.on('finish', function () {
  160. that.curActiveThread--; // 释放线程
  161. })
  162. stream.on('finish', function () {
  163. that.completeFileNum++;
  164. that.downloadProcess = that.completeFileNum / that.fileNum * 100;
  165. if (that.completeFileNum === that.fileNum) { // oss文件已下载完
  166. let sceneCode = that.sceneCode;
  167. let sceneJsonPath = path.join(__dirname, `tmpData/${sceneCode}/static/images/images${sceneCode}/sceneData.json`);
  168. let sceneCodePath = path.join(__dirname, `tmpData/${sceneCode}/code.txt`);
  169. fs.writeFile(sceneJsonPath, that.sceneInfo, function (err) { // 写入sceneInfo.json
  170. if (err) {
  171. return console.log(`写入文件出错: ${err}`);
  172. }
  173. fs.writeFile(sceneCodePath , sceneCode , function (err) { // 将场景码写入
  174. console.log('开始压缩' + that.sceneCode)
  175. // console.log(`scene: ${that.sceneCode}`);
  176. // let zipStream = fs.createWriteStream(`${__dirname}/tmpData/${that.sceneCode}/` + `/localData.zip`);
  177. let zipStream = fs.createWriteStream(`${__dirname}/tmpData/zip/` + `/${that.sceneCode}.zip`);
  178. pathConfig.localDataName = that.sceneCode;
  179. let archive = archiver('zip', {
  180. zlib: {
  181. level: 9
  182. }
  183. });
  184. zipStream.on('close', function () {
  185. let respData = 'archiver has been finalized and the output file descriptor has closed.';
  186. console.log(archive.pointer() + ' total bytes');
  187. console.log('文件压缩已写入完成');
  188. // 重置两个进度
  189. downloadPercent = 0;
  190. compressPercent = 0;
  191. });
  192. archive.on('progress', function (process) {
  193. compressPercent = that.zipProcess = process.fs.processedBytes / process.fs.totalBytes * 100;
  194. })
  195. archive.on('error', function (err) {
  196. console.log(err);
  197. })
  198. archive.pipe(zipStream);
  199. archive.directory(`${__dirname}/tmpData/${that.sceneCode}/`, false); // 放入static目录中
  200. archive.directory(`${__dirname}/static/page`, false); // 放入static目录中
  201. archive.directory(`${__dirname}/browser/`, false); // 将浏览器放入压缩包中
  202. // archive.directory(pathConfig.staticPath, 'static'); // 追加固定的文件资源
  203. // archive.directory(`${__dirname}/static/`, 'static');
  204. archive.finalize();
  205. })
  206. })
  207. }
  208. })
  209. this.curActiveThread++;
  210. }
  211. }
  212. downloader.prototype.getJson = function (url, callback) {
  213. console.log(url);
  214. http.get(url, res => {
  215. let body = '';
  216. res.on('data', chunk => {
  217. body += chunk;
  218. });
  219. res.on('end', () => {
  220. // let resp = JSON.parse(body);
  221. console.log(body);
  222. // callback(resp);
  223. })
  224. })
  225. }
  226. downloader.prototype.loadDynamicInfo = function (sceneCode) {
  227. for (let i = 0; i < pathConfig.dynamicPath.length; i++) {
  228. let time = new Date().getTime();
  229. let url = `${pathConfig.dynamicPath[i]}?num=${sceneCode}&t=${time}`;
  230. this.getJson(url);
  231. }
  232. }
  233. downloader.prototype.execute = function (data) {
  234. console.log(data);
  235. this.createRootFold(data);
  236. pathConfig.paths.forEach(path => {
  237. this.listDir(path + data, `static/`);
  238. })
  239. pathConfig.staticPath.forEach(path => {
  240. this.listDir(path, '');
  241. })
  242. // this.sceneInfo = data.sceneInfo
  243. // this.loadDynamicInfo(this.sceneInfo);
  244. }
  245. function start(response, data) {
  246. let sceneCode = data.sceneCode;
  247. let respData;
  248. console.log(`${__dirname}/${pathConfig.rootFold}/${sceneCode}/${pathConfig.localDataName}.zip`)
  249. if (fs.existsSync(`${__dirname}/${pathConfig.rootFold}/${sceneCode}/${pathConfig.localDataName}.zip`)) {
  250. //console.log(`${__dirname}/zip/${sceneCode}/${pathConfig.localDataName}.zip`)
  251. //if (fs.existsSync(`${__dirname}/zip/${sceneCode}/${pathConfig.localDataName}.zip`)) {
  252. respData = {
  253. sta: 1003, // 1003 文件已存在
  254. data: {
  255. percent: 100,
  256. url: `${pathConfig.serverPrefix}${pathConfig.rootFold}/${sceneCode}/${pathConfig.localDataName}.zip`
  257. },
  258. msg: '文件已存在, 直接返回url'
  259. };
  260. console.log('文件已存在, 直接返回url');
  261. } else {
  262. respData = {
  263. sta: 1002, // 1002
  264. data: {
  265. percent: 0
  266. },
  267. msg: '已加入任务队列'
  268. };
  269. taskCodeQueue.push(sceneCode);
  270. taskDownloaderQueue[sceneCode] = new downloader();
  271. taskDownloaderQueue[sceneCode].sceneCode = data.sceneCode;
  272. taskDownloaderQueue[sceneCode].sceneInfo = data.sceneInfo;
  273. }
  274. let json = JSON.stringify(respData)
  275. // response.writeHead(200, { "Content-Type": "application/json" });
  276. response.send(json);
  277. // response.end();
  278. }
  279. function downloadProcess(response, data) {
  280. let sceneCode = data.sceneCode;
  281. let downloader = taskDownloaderQueue[sceneCode];
  282. if (!downloader) return;
  283. if (downloader.downloadProcess < 100) {
  284. let respData = {
  285. sta: 1000, // 状态码 1000-文件正在下载 1001-文件正在压缩
  286. data: {
  287. percent: downloader.downloadProcess
  288. },
  289. msg: '文件下载中'
  290. };
  291. let json = JSON.stringify(respData)
  292. // response.writeHead(200, { "Content-Type": "application/json" });
  293. response.send(json);
  294. // response.end();
  295. } else {
  296. let respData = {
  297. sta: 1001, // 状态码 1000-文件正在下载 1001-文件正在压缩
  298. data: {
  299. percent: downloader.zipProcess
  300. },
  301. msg: '文件压缩中'
  302. };
  303. if (downloader.zipProcess === 100) {
  304. // respData.data.url = `${pathConfig.serverPrefix}${pathConfig.rootFold}/${downloader.sceneCode}/${pathConfig.localDataName}.zip`
  305. respData.data.url = `${pathConfig.serverPrefix}${pathConfig.rootFold}/zip/${pathConfig.localDataName}.zip`
  306. }
  307. let json = JSON.stringify(respData)
  308. // response.writeHead(200, { "Content-Type": "application/json" });
  309. response.send(json);
  310. // response.end();
  311. }
  312. }
  313. (function () {
  314. setInterval(function () {
  315. let taskCode = taskCodeQueue.shift();
  316. if (taskCode) {
  317. taskDownloaderQueue[taskCode].execute(taskCode);
  318. }
  319. }, 1000)
  320. })()
  321. exports.start = start;
  322. exports.downloadProcess = downloadProcess;