let OSS = require('ali-oss'); let fs = require('fs'); let path = require('path'); var archiver = require('archiver'); let request = require("request"); let http = require('http'); let compressPercent = 0; let taskCodeQueue = []; let taskDownloaderQueue = {}; let pathConfig = { ossPrefix: 'http://4dkankan.oss-cn-shenzhen.aliyuncs.com/', // oss前缀 // serverPrefix: 'http://192.168.0.61:8888/', // 服务器前缀 serverPrefix: 'https://test.4dkankan.com/back/down/server/controller/', // 服务器前缀 localDataName: 'localData', layer: './', // 层级 rootFold: 'tmpData', // 根目录文件夹 staticPath: [ // 静态数据路径 'static' ], paths: [ // images/data路径 'images/images', 'data/data' ], filters: ['tiles'], dynamicPath: [ // 一些使用api加载的数据, 提前写入json中 'http://pro.4dkankan.com/api/scene/getInfo' ], } function downloader() { this.objArr = []; this.completeFolds = 0; // 记录已经递归完成的目录, completeFolds == foldNum, 表示所有目录递归完成 this.fileNum = 0; // 需要下载的文件总数 this.completeFileNum = 0; // 已经下载完成的文件总数 this.threadNum = 10; // 同时并发的线程总数 this.curActiveThread = 0; this.sta = 0; this.downloadProcess = 0; // 下载进度 this.zipProcess = 0; // 压缩进度 this.sceneCode = ''; // 场景码 this.sceneInfo = ''; // 场景的其他json数据 this.downloadResponse = null; this.timer = null; // 定时器 this.dirPath = path.join(__dirname, pathConfig.rootFold); this.foldNum = pathConfig.paths.length + pathConfig.staticPath.length; // 记录目录的总数, 用于判断是否目录是否递归完成 this.client = new OSS({ // 配置阿里云oss region: 'oss-cn-shenzhen', accessKeyId: 'LTAIUrvuHqj8pvry', accessKeySecret: 'JLOVl0k8Ke0aaM8nLMMiUAZ3EiiqI4', bucket: '4dkankan', }) } downloader.prototype.createRootFold = function (data) { //console.log('pathpath' + this.dirPath); if (!fs.existsSync(this.dirPath)) { fs.mkdirSync(this.dirPath); } else { console.log('文件夹已存在'); } this.dirPath = path.join(this.dirPath, '/' + data); // 创建文件夹目录 if (!fs.existsSync(this.dirPath)) { fs.mkdirSync(this.dirPath); } else { console.log('文件夹已存在'); } // this.dirPath = path.join(this.dirPath, '/' + 'static'); // // 创建文件夹目录 // if (!fs.existsSync(this.dirPath)) { // fs.mkdirSync(this.dirPath); // } else { // console.log('文件夹已存在'); // } } downloader.prototype.listDir = async function (dir, prefixDir) { let that = this; for (let i = 0; i < pathConfig.filters.length; i++) { let filter = pathConfig.filters[i]; if (dir.indexOf(filter) > 0) { console.log('过滤的路径' + dir); that.completeFolds++; if (that.completeFolds === that.foldNum) { that.fileNum = that.objArr.length; that.timer = setInterval(that.download, 16); } return; } } let result = await this.client.list({ prefix: dir, delimiter: '/', 'max-keys': 1000 // 最大限制1000 }); // let dirArr = []; // if(staticSta){ // dirArr = `static/${dir}`.split('/'); // }else{ // } dirArr = `${prefixDir}${dir}`.split('/'); let tmpDir = ''; let childDirPath; dirArr.forEach((item) => { tmpDir += '/' + item; childDirPath = path.join(this.dirPath, '/' + tmpDir); if (!fs.existsSync(childDirPath)) { fs.mkdirSync(childDirPath); console.log('创建文件夹成功: ' + childDirPath); } else { console.log('文件夹已存在'); } }); result.objects && result.objects.forEach(async function (obj) { let downloadURL = pathConfig.ossPrefix + obj.name; let arr = obj.name.split('/'); let objName = arr[arr.length - 1]; if (objName === '') { return; // 过滤非法文件, 因为阿里云有时候会把文件夹当作文件返回, } // if(obj.name.indexOf('images') >-1){ // console.log(obj); // } // let writeURL = childDirPath + '/' + objName; let writeURL = path.join(childDirPath, objName); let tack = { downloadURL: downloadURL, writeURL: writeURL } that.objArr.push(tack); }); if (result.prefixes) { // 如果当前目录还有子目录存在, 则继续递归 this.foldNum--; // 移除当前目录 let that = this; result.prefixes.forEach(function (subDir) { that.foldNum++; // 记录当前目录的子目录 that.listDir(subDir, prefixDir); }); } else { // 当前目录已经递归完成 this.completeFolds++; console.log(this.completeFolds + ' ' + this.foldNum) if (this.completeFolds === this.foldNum) { // console.log(this.objArr) this.fileNum = this.objArr.length; this.timer = setInterval(this.download.bind(this), 16); } } } downloader.prototype.download = function () { if (this.objArr.length === 0) { clearInterval(this.timer); this.timer = null; return; } if (this.curActiveThread <= this.threadNum) { let task = this.objArr.shift(); let stream = fs.createWriteStream(task.writeURL); let readStream = request(task.downloadURL).on('error', function () { console.log("文件[" + task.downloadURL + "]下载出错 "); }).pipe(stream); let that = this; readStream.on('finish', function () { that.curActiveThread--; // 释放线程 }) stream.on('finish', function () { that.completeFileNum++; that.downloadProcess = that.completeFileNum / that.fileNum * 100; if (that.completeFileNum === that.fileNum) { // oss文件已下载完 let sceneCode = that.sceneCode; let sceneJsonPath = path.join(__dirname, `tmpData/${sceneCode}/static/images/images${sceneCode}/sceneData.json`); let sceneCodePath = path.join(__dirname, `tmpData/${sceneCode}/code.txt`); fs.writeFile(sceneJsonPath, that.sceneInfo, function (err) { // 写入sceneInfo.json if (err) { return console.log(`写入文件出错: ${err}`); } fs.writeFile(sceneCodePath , sceneCode , function (err) { // 将场景码写入 console.log('开始压缩' + that.sceneCode) // console.log(`scene: ${that.sceneCode}`); // let zipStream = fs.createWriteStream(`${__dirname}/tmpData/${that.sceneCode}/` + `/localData.zip`); let zipStream = fs.createWriteStream(`${__dirname}/tmpData/zip/` + `/${that.sceneCode}.zip`); pathConfig.localDataName = that.sceneCode; let archive = archiver('zip', { zlib: { level: 9 } }); zipStream.on('close', function () { let respData = 'archiver has been finalized and the output file descriptor has closed.'; console.log(archive.pointer() + ' total bytes'); console.log('文件压缩已写入完成'); // 重置两个进度 downloadPercent = 0; compressPercent = 0; }); archive.on('progress', function (process) { compressPercent = that.zipProcess = process.fs.processedBytes / process.fs.totalBytes * 100; }) archive.on('error', function (err) { console.log(err); }) archive.pipe(zipStream); archive.directory(`${__dirname}/tmpData/${that.sceneCode}/`, false); // 放入static目录中 archive.directory(`${__dirname}/static/page`, false); // 放入static目录中 archive.directory(`${__dirname}/browser/`, false); // 将浏览器放入压缩包中 // archive.directory(pathConfig.staticPath, 'static'); // 追加固定的文件资源 // archive.directory(`${__dirname}/static/`, 'static'); archive.finalize(); }) }) } }) this.curActiveThread++; } } downloader.prototype.getJson = function (url, callback) { console.log(url); http.get(url, res => { let body = ''; res.on('data', chunk => { body += chunk; }); res.on('end', () => { // let resp = JSON.parse(body); console.log(body); // callback(resp); }) }) } downloader.prototype.loadDynamicInfo = function (sceneCode) { for (let i = 0; i < pathConfig.dynamicPath.length; i++) { let time = new Date().getTime(); let url = `${pathConfig.dynamicPath[i]}?num=${sceneCode}&t=${time}`; this.getJson(url); } } downloader.prototype.execute = function (data) { console.log(data); this.createRootFold(data); pathConfig.paths.forEach(path => { this.listDir(path + data, `static/`); }) pathConfig.staticPath.forEach(path => { this.listDir(path, ''); }) // this.sceneInfo = data.sceneInfo // this.loadDynamicInfo(this.sceneInfo); } function start(response, data) { let sceneCode = data.sceneCode; let respData; console.log(`${__dirname}/${pathConfig.rootFold}/${sceneCode}/${pathConfig.localDataName}.zip`) if (fs.existsSync(`${__dirname}/${pathConfig.rootFold}/${sceneCode}/${pathConfig.localDataName}.zip`)) { //console.log(`${__dirname}/zip/${sceneCode}/${pathConfig.localDataName}.zip`) //if (fs.existsSync(`${__dirname}/zip/${sceneCode}/${pathConfig.localDataName}.zip`)) { respData = { sta: 1003, // 1003 文件已存在 data: { percent: 100, url: `${pathConfig.serverPrefix}${pathConfig.rootFold}/${sceneCode}/${pathConfig.localDataName}.zip` }, msg: '文件已存在, 直接返回url' }; console.log('文件已存在, 直接返回url'); } else { respData = { sta: 1002, // 1002 data: { percent: 0 }, msg: '已加入任务队列' }; taskCodeQueue.push(sceneCode); taskDownloaderQueue[sceneCode] = new downloader(); taskDownloaderQueue[sceneCode].sceneCode = data.sceneCode; taskDownloaderQueue[sceneCode].sceneInfo = data.sceneInfo; } let json = JSON.stringify(respData) // response.writeHead(200, { "Content-Type": "application/json" }); response.send(json); // response.end(); } function downloadProcess(response, data) { let sceneCode = data.sceneCode; let downloader = taskDownloaderQueue[sceneCode]; if (!downloader) return; if (downloader.downloadProcess < 100) { let respData = { sta: 1000, // 状态码 1000-文件正在下载 1001-文件正在压缩 data: { percent: downloader.downloadProcess }, msg: '文件下载中' }; let json = JSON.stringify(respData) // response.writeHead(200, { "Content-Type": "application/json" }); response.send(json); // response.end(); } else { let respData = { sta: 1001, // 状态码 1000-文件正在下载 1001-文件正在压缩 data: { percent: downloader.zipProcess }, msg: '文件压缩中' }; if (downloader.zipProcess === 100) { // respData.data.url = `${pathConfig.serverPrefix}${pathConfig.rootFold}/${downloader.sceneCode}/${pathConfig.localDataName}.zip` respData.data.url = `${pathConfig.serverPrefix}${pathConfig.rootFold}/zip/${pathConfig.localDataName}.zip` } let json = JSON.stringify(respData) // response.writeHead(200, { "Content-Type": "application/json" }); response.send(json); // response.end(); } } (function () { setInterval(function () { let taskCode = taskCodeQueue.shift(); if (taskCode) { taskDownloaderQueue[taskCode].execute(taskCode); } }, 1000) })() exports.start = start; exports.downloadProcess = downloadProcess;