8、一粒云二次封装与迁移 – 数据导出程序编写(接收端)
1、数据接收程序创建
首先创建项目目录
mkdir yliyunDMC && cd yliyunDMC && npm ini
拉取库
npm i figlet chalk axios minimist request request-progress progress -d && index.js
在index.js中引入所需的库。
const figlet = require('figlet'); const chalk = require('chalk'); const axios = require('axios'); const minimist = require('minimist'); const fs = require('fs'); const path = require('path'); const request = require('request'); const progress = require('request-progress'); const ProgressBar = require("progress");
const args = minimist(process.argv.slice(2)); const URL = args.url; const PATH = args.path; const SIGN = args.sign; const CACHE = args.cache; const HELP = args.help;
引入参数
URL DMS 地址
PATH 数据存放目录
SIGN DMS准入秘钥
CACHE 使用上一次的缓存记录
HELP 使用帮助
if(HELP !== undefined || URL === undefined || PATH === undefined || SIGN === undefined){ console.log("\n"); console.log("yliYunDmc --url " + chalk.yellow("<url>") + " --path " + chalk.yellow("<path>") + " --sign " + chalk.yellow("<sign>") + " --help " + chalk.yellow("<help>") + " --cache"); console.log("\n"); console.log(" --url <host> DMS Url"); console.log(" --path <path> 迁移数据目录"); console.log(" --sign <sign> DMS准入秘钥"); console.log(" --cache 使用上一次的进度"); console.log(" --help <help> 使用帮助"); console.log("\n"); console.log("\n"); return false; }
帮助信息的输出
MacBook-Pro yliYunDMC % node app.js --help yliYunDmc --url <url> --path <path> --sign <sign> --help <help> --cache --url <host> DMS Url --path <path> 迁移数据目录 --sign <sign> DMS准入秘钥 --cache 使用上一次的进度 --help <help> 使用帮助 MacBook-Pro yliYunDMC %
进行输出测试
console.log("\n"); const logo = figlet.textSync(" yliYun DMC"); console.log(logo); console.log("\n"); console.log(" 从yliYun DMS,读取结构并迁移数据到指定位置"); console.log(" 启动添加--help帮助"); console.log("\n"); console.log('##################################' + chalk.yellow.bold('校验') + '#################################'); console.log("\n");
UI显示,输出彩色字符。
const PersonalFilesLimit = 1; const PublicFilesLimit = 1; var PersonalFilesOffset = 0;= var PersonalFileDownNumber = 0; var PersonalFileNumber = 0; var PublicFilesOffset = 0; var PublicFileNumber = 0; var PublicFileDownNumber = 0; var cacheStatus = false;
初始化变量,PersonalFilesLimit个人文件每次读取数据库返回的文件量,PublicFilesLimit公共文件每次读取数据库返回的文件量。PersonalFilesOffset个人文件下载偏移量,完成一次PublicFilesLimit后进行一次PersonalFilesOffset + PublicFilesLimit的偏移,PersonalFileDownNumber个人文件已下载数量,PersonalFileNumber个人文件总量。公共文件变量与个人文件变量类似。
let cacheFileStatus = fs.existsSync(path.join(__dirname,"cache")); if(CACHE && cacheFileStatus){ let data = fs.readFileSync(path.join(__dirname,"cache")); data = JSON.parse(data.toString()); PersonalFilesOffset = data.PersonalFilesOffset; PublicFilesOffset = data.PublicFilesOffset; PublicFileNumber = data.PublicFileNumber; PublicFileDownNumber = data.PublicFileDownNumber; PersonalFileDownNumber = data.PersonalFileDownNumber; PersonalFileNumber = data.PersonalFileNumber; cacheStatus = true; } if(CACHE && !cacheFileStatus){ console.log("\n") console.log(chalk.yellow(" 注意!")) console.log(chalk.yellow(" 没有在目录下找到cache文件,--cache参数无效")) console.log("\n") return false; } if(cacheFileStatus && !CACHE){ console.log("\n") console.log(chalk.yellow(" 注意!")) console.log(chalk.yellow("检测到目录下存在cache数据文件,程序发生错误")) console.log(chalk.yellow("时自动生成该文件文件会自动保存上一次进度。如果")) console.log(chalk.yellow("想要使用该文件,请在启动指令中加入--cache参数,")) console.log(chalk.yellow("如果不想使用,请手动删除该文件")) console.log("\n") return false; } cacheStatus && console.log(" 1.缓存: " + chalk.green("[OK]")); !cacheStatus && console.log(" 1.缓存: " + chalk.green("[NO]"));
对缓存进行一个判断处理,如果当前目录下存在cache文件,并且使用了–cache指令,那么就读取cache并设置变量。当存在cache但没有指定–cache时进行一个提示避免因为误操作覆盖cache。当cache不存在但使用了–cache指令时,报错。缓存不对目录进行记录,因为目录生成会非常快。
/** * 抛出错误信息,立即保存进度,并退出程序 * @param {*} message */ const errorExit = function(message){ console.log("错误: " + chalk.red(message)); } /** * 记录缓存 */ const cacheWrite = function(){ fs.writeFileSync(path.join(__dirname,"cache"),JSON.stringify({ PersonalFileNumber: PersonalFileNumber, PublicFileNumber: PublicFileNumber, PersonalFilesOffset: PersonalFilesOffset, PublicFilesOffset: PublicFilesOffset, PublicFileNumber: PublicFileNumber, PublicFileDownNumber: PublicFileDownNumber, PersonalFileDownNumber: PersonalFileDownNumber, PersonalFileDownNumber: PersonalFileDownNumber })); }
错误抛出和缓存记录方法,当文件下载完成后进行一个缓存的记录。
1、个人目录结构
/** * 获取个人目录下可用目录的数量 * @param {*} url * @param {*} sign * @param {*} callback */ function getPersonalFolderNumber(url,sign,callback){ axios.post("http://"+ url + "/personal/folder/number",{ sign: sign }).then((res) => { if(res.data.status){ console.log("\n") callback && callback(res.data.data[0]["count(*)"]); }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确",false); } }).catch((err) => { console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启。0001",false); }) }
获取个人目录下面所有的可用目录数量方便进行定位和进度展示。
/** * 获取个人目录下可用目录的信息 * @param {*} url * @param {*} sign * @param {*} callback */ function getPersonalFolderInfo(url,sign,callback){ axios.post("http://"+ url + "/personal/folder/info",{ sign: sign }).then((res) => { if(res.data.status){ console.log("\n") callback && callback(res.data.data); }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确",false); } }).catch((err) => { console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启。0001",false); }); }
获取个人目录下可用目录的信息,所谓可用目录,就是没有被删除,状态正常的目录,请求回调的数据格式如下代码段,
获取数据之后,就可以使用parent_ids来反查出路径而creater_name则是用户名称,可以用来创建一级目录进行区分。
[ { parent_ids: '-', file_name: '测试', creater_name: '系统管理员' }, { parent_ids: '-3-', file_name: '一级目录', creater_name: '系统管理员' }, { parent_ids: '-3-23-', file_name: '二级目录', creater_name: '系统管理员' } ]
/** * 使用文件id标记的路径去查找出个人目录真实到路径 * @param {*} url * @param {*} sign * @param {*} callback */ function getPersonalFolderRealPath(url,sign,vPath,createrName,callback){ axios.post("http://"+ url + "/personal/folder/path",{ sign: sign, path: vPath, createrName: createrName }).then((res) => { if(res.data.status){ callback && callback(res.data.data); }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确",false); } }).catch((err) => { console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启。0001",false); }); }
parent_ids反查方法,查询返回字符串路径。
{ status: true, data: [ '系统管理员', '测试' ] } { status: true, data: [ '系统管理员', '测试', '一级目录' ] }
/** * 遍历获取 * @param {*} publicFolderInfo * @param {*} continuedCallback * @param {*} callback */ var getPersonalFolderPathYield = undefined; function * getPersonalFolderPath(url,sign,personalFolderInfo,continuedCallback,callback){ if(personalFolderInfo.length === 0){ callback && callback(); } if(personalFolderInfo.length !== 0){ let data = personalFolderInfo.shift(); if(data.parent_ids === "-"){ getPersonalFolderPathYield = getPersonalFolderPath(url,sign,personalFolderInfo,continuedCallback,callback); continuedCallback && continuedCallback([data.creater_name,data.file_name]); continuedCallback = undefined;callback = undefined; }else{ getPersonalFolderRealPath(url,sign,data.parent_ids,data.creater_name,(rPath) => { rPath.push(data.file_name); getPersonalFolderPathYield = getPersonalFolderPath(url,sign,personalFolderInfo,continuedCallback,callback); continuedCallback && continuedCallback(rPath); continuedCallback = undefined;callback = undefined; }) } } yield getPersonalFolderPathYield = getPersonalFolderPathYield(url,sign,publicFolderInfo,continuedCallback,callback); getPersonalFolderPathYield.next(); continuedCallback = undefined;callback = undefined; }
对路径进行一个分析,如果data.parent_ids是-则代表是根路径,如果不是则交给getPersonalFolderRealPath反查路径。
[ { parent_ids: '-', file_name: '测试', creater_name: '系统管理员' }, { parent_ids: '-3-', file_name: '一级目录', creater_name: '系统管理员' }, { parent_ids: '-3-23-', file_name: '二级目录', creater_name: '系统管理员' } ] [ { parent_ids: '-3-', file_name: '一级目录', creater_name: '系统管理员' }, { parent_ids: '-3-23-', file_name: '二级目录', creater_name: '系统管理员' } ] [ { parent_ids: '-3-23-', file_name: '二级目录', creater_name: '系统管理员' } ]
/** * 创建个人文件夹结构 * @param {*} url * @param {*} sign * @param {*} localPath * @param {*} callback */ function createPersonalFolder(url,sign,localPath,callback){ getPersonalFolderNumber(url,sign,(personalFolderNumber) => { PersonalFolderNumber = personalFolderNumber; getPersonalFolderInfo(url,sign,(personalFolderInfo) => { PersonalFolderInfo = personalFolderInfo; getPersonalFolderPathYield = getPersonalFolderPath(url,sign,PersonalFolderInfo,(folders) => { !fs.existsSync(path.join(localPath,"个人")) && fs.mkdirSync(path.join(localPath,"个人")); localFolderCreate(path.join(localPath,"个人"),folders,() => { getPersonalFolderPathYield.next(); }); },() => { callback && callback(); }) getPersonalFolderPathYield.next(); }); }); }
获取到文件在系统中的路径之后,还需要将它拼接为本地的路径。程序设计时,将公共和个人文件分别存放于“公共”“个人”文件下,而个人文件夹中将不同用户创建的文件分类存放,所以最终路径为/个人/xxx用户/文件结构…./….当成功拼接成此路径之后,就可以进行目录的创建。
MacBook-Pro yliYunDMC % node app.js --url 192.168.0.22:1115 --path /Users/fcy/Downloads/yliYunData --sign 123456 _ ___ __ ____ __ __ ____ _ _| (_) \ / / _ _ __ | _ \| \/ |/ ___| | | | | | |\ V / | | | '_ \ | | | | |\/| | | | |_| | | | | || |_| | | | | | |_| | | | | |___ \__, |_|_| |_| \__,_|_| |_| |____/|_| |_|\____| |___/ 从yliYun DMS,读取结构并迁移数据到指定位置 启动添加--help帮助 ##################################校验################################# 1.缓存: [NO] 2.DMS通讯: [OK] 创建目录 : /Users/xxx/Downloads/yliYunData/个人/系统管理员 创建目录 : /Users/xxx/Downloads/yliYunData/个人/系统管理员/测试 创建目录 : /Users/xxx/Downloads/yliYunData/个人/系统管理员/测试/一级目录 创建目录 : /Users/xxx/Downloads/yliYunData/个人/系统管理员/测试/一级目录/二级目录 个人目录结构完成!
上面为创建个人文件结构的运行测试。
/** * 获取个人目录下有效文件的数量 * @param {*} url * @param {*} sign * @param {*} callback */ function getPersonalFileNumber(url,sign,callback){ axios.post("http://"+ url + "/personal/files/number",{ sign: sign }).then((res) => { if(res.data.status){ !cacheStatus && (PersonalFileNumber = res.data.data[0]["count(*)"] - 1); console.log("\n") callback && callback(res.data.data[0]["count(*)"]); }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确",false); } }).catch((err) => { console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启。",false); }) }
建立完个人文件夹之后就可以对个人文件进行接收。首先是获取个人目录下所有的有效文件的数量,这样可以对进度进行一个展示。
/** * 每次1个文件的进度获取个人文件的基本信息 * @param {*} url DMS地址 * @param {*} sign 通讯秘钥 * @param {*} continuedCallback 每次读取完毕后的回调 * @param {*} callback 完全读取完毕后的回调 */ var getPersonalFileInfoYield = undefined; function * getPersonalFileInfo(url,sign,continuedCallback,callback){ if(PersonalFilesOffset > PersonalFileNumber){ callback && callback(); return false; }else{ axios.post("http://"+ url + "/personal/files/info",{ limit: PersonalFilesLimit, offset: PersonalFilesOffset, sign: sign }).then((res) => { if(res.data.status){ PersonalFilesOffset = PersonalFilesOffset + PersonalFilesLimit; getPersonalFileInfoYield = getPersonalFileInfo(url,sign,continuedCallback,callback); continuedCallback && continuedCallback(res.data.data); continuedCallback = undefined; callback = undefined; }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确1",false); } }).catch((err) => { console.log(err) console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启。1",false); }) } yield; getPersonalFileInfoYield = getPersonalFileInfoYield(url,sign,continuedCallback,callback); continuedCallback = undefined; callback = undefined; getPersonalFileInfoYield.next(); }
每次从服务端获取一个需要下载的文件信息。
{ status: true, data: [ { fs_file_id: 142, parent_ids: '-3-23-24-', file_name: 'Diet Cola.js', creater_name: '系统管理员' } ] }
每次获取回传的数据如上,该文件在fs_file表中的id,parent_ids字段拿来反查路径,file_name为文件的名称,文件存入文件系统之后命名是无法分辨的,需要靠此字段进行重命名。creater_name则为上传这个文件的用户名称,依靠这个来进行文件按用户归类存放。
/** * 获取个人文件在本地中应该所处的路径 * @param {*} url DMS地址 * @param {*} sign 通讯秘钥 * @param {*} filesInfo getPersonalFileInfo返回的文件信息 * @param {*} continuedCallback 每次读取完毕后的回调 * @param {*} callback 完全读取完毕后的回调 */ var getPersonalFileDfsPathYield = undefined; function * getPersonalFileLocalPath(url,sign,filesInfo,continuedCallback,callback){ if(filesInfo.length === 0){ callback && callback(); } if(filesInfo.length !== 0){ let data = filesInfo.shift(); if(data.parent_ids === "-"){ getPersonalFileDfsPathYield = getPersonalFileLocalPath(url,sign,filesInfo,continuedCallback,callback); continuedCallback && continuedCallback({ name: data.file_name, fsId: data.fs_file_id, path: [], createrName: data.creater_name }); continuedCallback = undefined; callback = undefined; } if(data.parent_ids !== "-"){ axios.post("http://"+ url + "/personal/files/path",{ path: data.parent_ids, sign: sign }).then((res) => { if(res.data.status){ getPersonalFileDfsPathYield = getPersonalFileLocalPath(url,sign,filesInfo,continuedCallback,callback); continuedCallback && continuedCallback({ name: data.file_name, fsId: data.fs_file_id, path: res.data.data, createrName: data.creater_name }); continuedCallback = undefined; callback = undefined; }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确",false); } }).catch((err) => { console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启。",false); }) } } yield; getPersonalFileDfsPathYield = getPersonalFileLocalPath(url,sign,filesInfo,continuedCallback,callback); continuedCallback = undefined; callback = undefined; getPersonalFileDfsPathYield.next(); }
对parent_ids进行反查,查出路径并返回。
/** * 下载公共文件 * @param {*} url * @param {*} sign * @param {*} callback */ function DownloadPersonalFiles(url,sign,localPath,callback){ getPersonalFileNumber(url,sign,() => { getPersonalFileInfoYield = getPersonalFileInfo(url,sign,(filesInfo) => { getPersonalFileDfsPathYield = getPersonalFileLocalPath(url,sign,filesInfo,(filePathInfo) => { //地址补全 filePathInfo.path.unshift(filePathInfo.createrName); filePathInfo.path.unshift("个人"); filePathInfo.path.unshift(localPath); filePathInfo.path.push(filePathInfo.name); let localFilePathArray = path.join(...filePathInfo.path).split(''); if(localFilePathArray.length > 48){ localFilePathArray.splice(10,10 + (localFilePathArray.length - 48)); localFilePathArray.splice(10, 0," .....") } console.log("个人文件 : " + chalk.green(filePathInfo.name)); console.log("保存路径 : " + chalk.green(localFilePathArray.join(''))); console.log("总进度 : " + chalk.green(PersonalFileDownNumber) + "/" + chalk.red(PersonalFileNumber)); let percent = 0;let bar = new ProgressBar("进度 : [ :bar ] :percent%", { total: 50 });bar.tick(0); personalFilesDownload(url,sign,filePathInfo.fsId,path.join(...filePathInfo.path),(p) => { bar.tick(((p.percent - percent) * 100) / 2,{ isSize: p.speed }); percent = p.percent; },() => { bar.tick(51); console.log("\n"); PersonalFileDownNumber++; cacheWrite(); getPersonalFileDfsPathYield.next(); }) },() => { getPersonalFileInfoYield.next(); }) getPersonalFileDfsPathYield.next(); },() => { callback && callback(); }); getPersonalFileInfoYield.next(); }); }
最初始的路径是以数组的方式存放,需要将它拼接成正常的路径,并交给personalFilesDownload函数进行下载,此函数会不停反馈下载进度。
/** * 下载远程文件 * @param {*} url 地址 * @param {*} localPath 下载目标路径 * @param {*} downProgress 进度 * @param {*} callback 下载完毕回调 */ function personalFilesDownload(url,sign,fsId,localPath,downProgress,callback){ axios.post("http://"+ url + "/personal/files/dfs/path",{ id: fsId, sign: sign }).then((res) => { if(res.data.status){ let [file] = res.data.data; progress(request("http://" + url + "/" + file.file_name),{ //progress(request("http://localhost/test.txt"),{ delay: 1000 }).on('progress', function (state) { downProgress && downProgress(state) }).on('error', function (err) { errorExit("下载: " + url + "时发生错误,错误信息:" + err,true); }).on('end', function () { callback && callback(); }).pipe(fs.createWriteStream(localPath)); }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确1",false); } }).catch((err) => { //console.log(err) console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启1。",false); }) }
从服务端下载文件。
2、公共目录结构
/** * 获取公共目录下可用目录的数量 * @param {*} url * @param {*} sign * @param {*} callback */ function getPublicFolderNumber(url,sign,callback){ axios.post("http://"+ url + "/public/folder/number",{ sign: sign }).then((res) => { console.log(" 2.DMS通讯: " + chalk.green("[OK]")); if(res.data.status){ console.log("\n") callback && callback(res.data.data[0]["count(*)"]); }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确",false); } }).catch((err) => { console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启。0001",false); }) }
获取公共目录下面所有的可用目录数量方便进行定位和进度展示。
/** * 获取公共目录下可用目录的信息 * @param {*} url * @param {*} sign * @param {*} callback */ function getPublicFolderInfo(url,sign,callback){ axios.post("http://"+ url + "/public/folder/info",{ sign: sign }).then((res) => { if(res.data.status){ console.log("\n") callback && callback(res.data.data); }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确",false); } }).catch((err) => { console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启。0001",false); }); }
获取公共目录下可用目录的信息,所谓可用目录,就是没有被删除,状态正常的目录,请求回调的数据格式如下代码段,
获取数据之后,就可以使用parent_ids来反查出路径。
/** * 使用文件id标记的路径去查找出公共目录真实到路径 * @param {*} url * @param {*} sign * @param {*} callback */ function getPublicFolderRealPath(url,sign,vPath,callback){ axios.post("http://"+ url + "/public/folder/path",{ sign: sign, path: vPath }).then((res) => { if(res.data.status){ callback && callback(res.data.data); }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确",false); } }).catch((err) => { console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启。0001",false); }); }
parent_ids反查方法,查询返回字符串路径。
/** * 创建公共文件夹结构 * @param {*} url * @param {*} sign * @param {*} localPath * @param {*} callback */ function createPublicFolder(url,sign,localPath,callback){ getPublicFolderNumber(url,sign,(publicFolderNumber) => { PublicFolderNumber = publicFolderNumber; getPublicFolderInfo(url,sign,(publicFolderInfo) => { PublicFolderInfo = publicFolderInfo; getPublicFolderPathYield = getPublicFolderPath(url,sign,PublicFolderInfo,(folders) => { !fs.existsSync(path.join(localPath,"公共")) && fs.mkdirSync(path.join(localPath,"公共")); localFolderCreate(path.join(localPath,"公共"),folders,() => { getPublicFolderPathYield.next(); }); },() => { callback && callback(); }) getPublicFolderPathYield.next(); }); }) }
按反查路径进行目录创建,当存在多级目录时,调用下面的方法进行创建。
/** * 按照路径创建多级目录 * @param {*} path * @param {*} callback */ function localFolderCreate(localPath,folders,callback){ let folderPath = localPath; folders.map((item) => { folderPath = path.join(folderPath,item); let folderPathStatus = fs.existsSync(folderPath); if(!folderPathStatus){ console.log("创建目录 : " + chalk.green(folderPath)); fs.mkdirSync(folderPath) } }); callback && callback(); }
2、目录结构测试
MacBook-Pro yliYunDMC % node app.js --url 192.168.0.22:1115 --path /Users/xxx/Downloads/yliYunData --sign 123456 _ ___ __ ____ __ __ ____ _ _| (_) \ / / _ _ __ | _ \| \/ |/ ___| | | | | | |\ V / | | | '_ \ | | | | |\/| | | | |_| | | | | || |_| | | | | | |_| | | | | |___ \__, |_|_| |_| \__,_|_| |_| |____/|_| |_|\____| |___/ 从yliYun DMS,读取结构并迁移数据到指定位置 启动添加--help帮助 ##################################校验################################# 1.缓存: [NO] 2.DMS通讯: [OK] 创建目录 : /Users/xxx/Downloads/yliYunData/公共/测试 创建目录 : /Users/xxx/Downloads/yliYunData/公共/测试/一级目录 创建目录 : /Users/xxx/Downloads/yliYunData/公共/测试/一级目录/二级目录 公共目录结构完成! 3 创建目录 : /Users/xxx/Downloads/yliYunData/个人/系统管理员 创建目录 : /Users/xxx/Downloads/yliYunData/个人/系统管理员/测试 创建目录 : /Users/xxx/Downloads/yliYunData/个人/系统管理员/测试/一级目录 创建目录 : /Users/xxx/Downloads/yliYunData/个人/系统管理员/测试/一级目录/二级目录 个人目录结构完成!
执行DMC,完成目录创建。
校验
3、个人文件
/** * 获取个人目录下有效文件的数量 * @param {*} url * @param {*} sign * @param {*} callback */ function getPersonalFileNumber(url,sign,callback){ axios.post("http://"+ url + "/personal/files/number",{ sign: sign }).then((res) => { if(res.data.status){ !cacheStatus && (PersonalFileNumber = res.data.data[0]["count(*)"] - 1); console.log("\n") callback && callback(res.data.data[0]["count(*)"]); }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确",false); } }).catch((err) => { console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启。",false); }) }
获取个人账户下的所有有效文件数量,用作进度展示和缓存。
/** * 每次1个文件的进度获取个人文件的基本信息 * @param {*} url DMS地址 * @param {*} sign 通讯秘钥 * @param {*} continuedCallback 每次读取完毕后的回调 * @param {*} callback 完全读取完毕后的回调 */ var getPersonalFileInfoYield = undefined; function * getPersonalFileInfo(url,sign,continuedCallback,callback){ if(PersonalFilesOffset > PersonalFileNumber){ callback && callback(); return false; }else{ axios.post("http://"+ url + "/personal/files/info",{ limit: PersonalFilesLimit, offset: PersonalFilesOffset, sign: sign }).then((res) => { if(res.data.status){ console.log(res.data) PersonalFilesOffset = PersonalFilesOffset + PersonalFilesLimit; getPersonalFileInfoYield = getPersonalFileInfo(url,sign,continuedCallback,callback); continuedCallback && continuedCallback(res.data.data); continuedCallback = undefined; callback = undefined; }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确1",false); } }).catch((err) => { console.log(err) console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启。1",false); }) } yield; getPersonalFileInfoYield = getPersonalFileInfoYield(url,sign,continuedCallback,callback); continuedCallback = undefined; callback = undefined; getPersonalFileInfoYield.next(); }
获取文件的详细信息,fs_file_id,parent_ids,file_name和creater_name;
{ status: true, data: [ { fs_file_id: 145, parent_ids: '-3-23-24-', file_name: 'Crazy.js', creater_name: '系统管理员' } ] }
/** * 获取个人文件在本地中应该所处的路径 * @param {*} url DMS地址 * @param {*} sign 通讯秘钥 * @param {*} filesInfo getPersonalFileInfo返回的文件信息 * @param {*} continuedCallback 每次读取完毕后的回调 * @param {*} callback 完全读取完毕后的回调 */ var getPersonalFileDfsPathYield = undefined; function * getPersonalFileLocalPath(url,sign,filesInfo,continuedCallback,callback){ if(filesInfo.length === 0){ callback && callback(); } if(filesInfo.length !== 0){ let data = filesInfo.shift(); if(data.parent_ids === "-"){ getPersonalFileDfsPathYield = getPersonalFileLocalPath(url,sign,filesInfo,continuedCallback,callback); continuedCallback && continuedCallback({ name: data.file_name, fsId: data.fs_file_id, path: [], createrName: data.creater_name }); continuedCallback = undefined; callback = undefined; } if(data.parent_ids !== "-"){ axios.post("http://"+ url + "/personal/files/path",{ path: data.parent_ids, sign: sign }).then((res) => { if(res.data.status){ getPersonalFileDfsPathYield = getPersonalFileLocalPath(url,sign,filesInfo,continuedCallback,callback); continuedCallback && continuedCallback({ name: data.file_name, fsId: data.fs_file_id, path: res.data.data, createrName: data.creater_name }); continuedCallback = undefined; callback = undefined; }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确",false); } }).catch((err) => { console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启。",false); }) } } yield; getPersonalFileDfsPathYield = getPersonalFileLocalPath(url,sign,filesInfo,continuedCallback,callback); continuedCallback = undefined; callback = undefined; getPersonalFileDfsPathYield.next(); }
获取到parent_ids后传给getPersonalFileLocalPath方法处理出本地路径,下面为处理后的结果。根据这个数据可以得出路径为 个人/系统管理员/测试/一级目录/二级目录/Diet Cola.js
{ name: 'Diet Cola.js', fsId: 142, path: [ '测试', '一级目录', '二级目录' ], createrName: '系统管理员' }
/** * 下载远程文件 * @param {*} url 地址 * @param {*} localPath 下载目标路径 * @param {*} downProgress 进度 * @param {*} callback 下载完毕回调 */ function personalFilesDownload(url,sign,fsId,localPath,downProgress,callback){ axios.post("http://"+ url + "/personal/files/dfs/path",{ id: fsId, sign: sign }).then((res) => { if(res.data.status){ let [file] = res.data.data; progress(request("http://" + url + "/" + file.file_name),{ delay: 1000 }).on('progress', function (state) { downProgress && downProgress(state) }).on('error', function (err) { errorExit("下载: " + url + "时发生错误,错误信息:" + err,true); }).on('end', function () { callback && callback(); }).pipe(fs.createWriteStream(localPath)); }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确1",false); } }).catch((err) => { //console.log(err) console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启1。",false); }) }
获取到本地路径之后就需要获取远程路径,将文件下载下来放入本地路径就完成了一次文件拉取。使用获取到的fsId值去fs_file表查出文件在文件系统中的路径。
{ file_name: 'group1/M00/00/01/wKgAFmPEuJ6AEr_jAABagZr3bXE6759.js', file_md5: 'c0de229948cc3d94c82c91c6a2455748' }
因为在DMS中已经使用express以下面的方式挂载了文件系统存储节点,所以直接访问http://xxx.xxx.xx.xxx:xxx/group1/M00/00/01/wKgAFmPEuJ6AEr_jAABagZr3bXE6759.js即可下载该文件。
app.use(`/组/子节点`,express.static(挂载磁盘路径))
/** * 下载个人文件,并展示进度 * @param {*} url * @param {*} sign * @param {*} callback */ function DownloadPersonalFiles(url,sign,localPath,callback){ getPersonalFileNumber(url,sign,() => { getPersonalFileInfoYield = getPersonalFileInfo(url,sign,(filesInfo) => { getPersonalFileDfsPathYield = getPersonalFileLocalPath(url,sign,filesInfo,(filePathInfo) => { //地址补全 filePathInfo.path.unshift(filePathInfo.createrName); filePathInfo.path.unshift("个人"); filePathInfo.path.unshift(localPath); filePathInfo.path.push(filePathInfo.name); let localFilePathArray = path.join(...filePathInfo.path).split(''); if(localFilePathArray.length > 48){ localFilePathArray.splice(10,10 + (localFilePathArray.length - 48)); localFilePathArray.splice(10, 0," .....") } console.log("个人文件 : " + chalk.green(filePathInfo.name)); console.log("保存路径 : " + chalk.green(localFilePathArray.join(''))); console.log("总进度 : " + chalk.green(PersonalFileDownNumber) + "/" + chalk.red(PersonalFileNumber)); let percent = 0;let bar = new ProgressBar("进度 : [ :bar ] :percent%", { total: 50 });bar.tick(0); personalFilesDownload(url,sign,filePathInfo.fsId,path.join(...filePathInfo.path),(p) => { bar.tick(((p.percent - percent) * 100) / 2,{ isSize: p.speed }); percent = p.percent; },() => { bar.tick(51); console.log("\n"); PersonalFileDownNumber++; cacheWrite(); getPersonalFileDfsPathYield.next(); }) },() => { getPersonalFileInfoYield.next(); }) getPersonalFileDfsPathYield.next(); },() => { callback && callback(); }); getPersonalFileInfoYield.next(); }); }
进行个人文件下载,展示进度,并存储到对应路径。下载结果可以使用file_md5中的数据进行文件完整性校验。但此程序中不做此功能。
个人文件 : 测试.docx 保存路径 : /Users/xxx .....iYunData/个人/系统管理员/测试/测试.docx 总进度 : 0/305 进度 : [ ================================================== ] 100% 个人文件 : Diet Cola.js 保存路径 : /Users/xxx .....理员/测试/一级目录/二级目录/Diet Cola.js 总进度 : 1/305 进度 : [ ================================================== ] 100% 个人文件 : Crazy.js 保存路径 : /Users/xxx ...../系统管理员/测试/一级目录/二级目录/Crazy.js 总进度 : 2/305 进度 : [ ================================================== ] 100% 个人文件 : Def Leppard.js 保存路径 : /Users/xxx ...../测试/一级目录/二级目录/Def Leppard.js 总进度 : 3/305 进度 : [ ================================================== ] 100% 个人文件 : Decimal.js 保存路径 : /Users/xxx .....统管理员/测试/一级目录/二级目录/Decimal.js 总进度 : 4/305 进度 : [ ================================================== ] 100% 个人文件 : Cursive.js 保存路径 : /Users/xxx .....统管理员/测试/一级目录/二级目录/Cursive.js 总进度 : 5/305 进度 : [ ================================================== ] 100% 个人文件 : Wireshark-win64-3.6.10.exe 保存路径 : /Users/xxx .....试/Wireshark-win64-3.6.10.exe 总进度 : 6/305 进度 : [ ================================================== ] 100%
进行测试。
查看下载数据。
4、公共文件
公共账户文件获取流程和个人账户一样,不过公共文件不涉及用户分割。
/** * 获取公共目录下有效文件的数量 * @param {*} url * @param {*} sign * @param {*} callback */ function getPublicFileNumber(url,sign,callback){ axios.post("http://"+ url + "/public/files/number",{ sign: sign }).then((res) => { if(res.data.status){ !cacheStatus && (PublicFileNumber = res.data.data[0]["count(*)"] - 1); console.log("\n") callback && callback(res.data.data[0]["count(*)"]); }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确",false); } }).catch((err) => { console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启。",false); }) }
获取公共账户下的所有有效文件数量,用作进度展示和缓存。
/** * 每次1个文件的进度获取公共文件的基本信息 * @param {*} url DMS地址 * @param {*} sign 通讯秘钥 * @param {*} continuedCallback 每次读取完毕后的回调 * @param {*} callback 完全读取完毕后的回调 */ var getPublicFileInfoYield = undefined; function * getPublicFileInfo(url,sign,continuedCallback,callback){ if(PublicFilesOffset > PublicFileNumber){ callback && callback(); return false; }else{ axios.post("http://"+ url + "/public/files/info",{ limit: PublicFilesLimit, offset: PublicFilesOffset, sign: sign }).then((res) => { if(res.data.status){ PublicFilesOffset = PublicFilesOffset + PublicFilesLimit; getPublicFileInfoYield = getPublicFileInfo(url,sign,continuedCallback,callback); continuedCallback && continuedCallback(res.data.data); continuedCallback = undefined; callback = undefined; }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确1",false); } }).catch((err) => { console.log(err) console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启。1",false); }) } yield; getPublicFileInfoYield = getPublicFileInfoYield(url,sign,continuedCallback,callback); continuedCallback = undefined; callback = undefined; getPublicFileInfoYield.next(); }
获取文件的详细信息,fs_file_id,parent_ids,file_name,与个人文件不同的是,不需要使用creater_name字段。
/** * 获取公共文件在本地中应该所处的路径 * @param {*} url DMS地址 * @param {*} sign 通讯秘钥 * @param {*} filesInfo getPublicFileInfo返回的文件信息 * @param {*} continuedCallback 每次读取完毕后的回调 * @param {*} callback 完全读取完毕后的回调 */ var getPublicFileDfsPathYield = undefined; function * getPublicFileLocalPath(url,sign,filesInfo,continuedCallback,callback){ if(filesInfo.length === 0){ callback && callback(); } if(filesInfo.length !== 0){ let data = filesInfo.shift(); if(data.parent_ids === "-"){ getPublicFileDfsPathYield = getPublicFileLocalPath(url,sign,filesInfo,continuedCallback,callback); continuedCallback && continuedCallback({ name: data.file_name, fsId: data.fs_file_id, path: res.data.data }); continuedCallback = undefined; callback = undefined; } if(data.parent_ids !== "-"){ axios.post("http://"+ url + "/public/files/path",{ path: data.parent_ids, sign: sign }).then((res) => { if(res.data.status){ getPublicFileDfsPathYield = getPublicFileLocalPath(url,sign,filesInfo,continuedCallback,callback); continuedCallback && continuedCallback({ name: data.file_name, fsId: data.fs_file_id, path: res.data.data }); continuedCallback = undefined; callback = undefined; }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确",false); } }).catch((err) => { console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启。",false); }) } } yield; getPublicFileDfsPathYield = getPublicFileLocalPath(url,sign,filesInfo,continuedCallback,callback); continuedCallback = undefined; callback = undefined; getPublicFileDfsPathYield.next(); }
获取到parent_ids后传给getPersonalFileLocalPath方法处理出本地路径,下面为处理后的结果。根据这个数据可以得出路径为 公共/xxx/xxxx/xxxx/Diet Cola.js
/** * 下载远程文件 * @param {*} url 地址 * @param {*} localPath 下载目标路径 * @param {*} downProgress 进度 * @param {*} callback 下载完毕回调 */ function publicFilesDownload(url,sign,fsId,localPath,downProgress,callback){ axios.post("http://"+ url + "/public/files/dfs/path",{ id: fsId, sign: sign }).then((res) => { if(res.data.status){ let [file] = res.data.data; progress(request("http://" + url + "/" + file.file_name),{ delay: 1000 }).on('progress', function (state) { downProgress && downProgress(state) }).on('error', function (err) { errorExit("下载: " + url + "时发生错误,错误信息:" + err,true); }).on('end', function () { callback && callback(); }).pipe(fs.createWriteStream(localPath)); }else{ console.log("\n"); errorExit("无法从DMS获取文件数据,请确认通讯秘钥是否正确1",false); } }).catch((err) => { console.log("\n"); errorExit("与DMS通讯失败,请检查网络链接是否正常,DMS对应端口是否开启1。",false); }) }
获取到本地路径之后就需要获取远程路径,将文件下载下来放入本地路径就完成了一次文件拉取。使用获取到的fsId值去fs_file表查出文件在文件系统中的路径。
/** * 下载公共文件 * @param {*} url * @param {*} sign * @param {*} callback */ function DownloadPublicFiles(url,sign,localPath,callback){ getPublicFileNumber(url,sign,() => { getPublicFileInfoYield = getPublicFileInfo(url,sign,(filesInfo) => { getPublicFileDfsPathYield = getPublicFileLocalPath(url,sign,filesInfo,(filePathInfo) => { //地址补全 filePathInfo.path.unshift("公共");filePathInfo.path.unshift(localPath);filePathInfo.path.push(filePathInfo.name); let localFilePathArray = path.join(...filePathInfo.path).split(''); if(localFilePathArray.length > 48){ localFilePathArray.splice(10,10 + (localFilePathArray.length - 48)); localFilePathArray.splice(10, 0," ....."); } console.log("公共文件 : " + chalk.green(filePathInfo.name)); console.log("保存路径 : " + chalk.green(localFilePathArray.join(''))); console.log("总进度 : " + chalk.green(PublicFileDownNumber) + "/" + chalk.red(PublicFileNumber)); let percent = 0;let bar = new ProgressBar("进度 : [ :bar ] :percent%", { total: 50 });bar.tick(0); publicFilesDownload(url,sign,filePathInfo.fsId,path.join(...filePathInfo.path),(p) => { bar.tick(((p.percent - percent) * 100) / 2,{ isSize: p.speed }); percent = p.percent; },() => { bar.tick(51); console.log("\n"); PublicFileDownNumber++; cacheWrite(); getPublicFileDfsPathYield.next(); }) },() => { getPublicFileInfoYield.next(); }) getPublicFileDfsPathYield.next(); },() => { callback && callback(); }); getPublicFileInfoYield.next(); }); }
进行公共文件下载,展示进度,并存储到对应路径。下载结果可以使用file_md5中的数据进行文件完整性校验。但此程序中不做此功能。
createPublicFolder(URL,SIGN,PATH,() => { createPersonalFolder(URL,SIGN,PATH,() => { DownloadPublicFiles(URL,SIGN,PATH,() => { console.log(`公共数据下载完成!共下载文件${PublicFileDownNumber--}个`); DownloadPersonalFiles(URL,SIGN,PATH,() => { console.log(`个人数据下载完成!共下载文件${PersonalFileDownNumber--}个`); }) }); }) });
执行创建目录和拉取文件。
5、测试
root@yliyun:/opt/yliYunDMS# ./bin/node-linux-x64 app.js --P yliyun123@ --s 123456 --dg group1 --dp M00=/yliyun_data_01/data@M01=/yliyun_data_02/data@M02=/yliyun_data_03/data@M03=/yliyun_data_04/data _ ___ __ ____ __ __ ____ _ _| (_) \ / / _ _ __ | _ \| \/ / ___| | | | | | |\ V / | | | '_ \ | | | | |\/| \___ \ | |_| | | | | || |_| | | | | | |_| | | | |___) | \__, |_|_| |_| \__,_|_| |_| |____/|_| |_|____/ |___/ 配合yliYun DMC,将YliYun系统中的目录结构与文件 完整的迁移至外部 --help帮助 #################################数据库################################ 1.地址: localhost 2.用户名: root 3.密码: yliyun123@ 4.端口: 3306 5.连接表: yliyun 5.数据库: [OK] 6.目录记录: 3 7.文件记录: 519 ##################################服务################################# 1.迁移服务: [OK] 2.地址: :::1115 3.密钥: 123456 ##################################存储################################# 组: group1 M00: /yliyun_data_01/data M01: /yliyun_data_02/data M02: /yliyun_data_03/data M03: /yliyun_data_04/data
服务端执行DMS
MacBook-Pro yliYunDMC % node app.js --url 192.168.0.22:1115 --path /Users/xxx/Downloads/yliYunData --sign 123456 _ ___ __ ____ __ __ ____ _ _| (_) \ / / _ _ __ | _ \| \/ |/ ___| | | | | | |\ V / | | | '_ \ | | | | |\/| | | | |_| | | | | || |_| | | | | | |_| | | | | |___ \__, |_|_| |_| \__,_|_| |_| |____/|_| |_|\____| |___/ 从yliYun DMS,读取结构并迁移数据到指定位置 启动添加--help帮助 ##################################校验################################# 1.缓存: [NO] 2.DMS通讯: [OK] 公共文件 : .travis(1).yml 保存路径 : /Users/xxx ...../测试/一级目录/二级目录/.travis(1).yml 总进度 : 0/4 进度 : [ ================================================== ] 100%% 公共文件 : .eslintrc 保存路径 : /Users/xxx .....ta/公共/测试/一级目录/二级目录/.eslintrc 总进度 : 1/4 进度 : [ ================================================== ] 100%% 公共文件 : .editorconfig 保存路径 : /Users/xxx .....共/测试/一级目录/二级目录/.editorconfig 总进度 : 2/4 进度 : [ ================================================== ] 100%% 公共文件 : .jscs.json 保存路径 : /Users/xxx .....a/公共/测试/一级目录/二级目录/.jscs.json 总进度 : 3/4 进度 : [ ================================================== ] 100%% 公共文件 : .travis(2).yml 保存路径 : /Users/xxx ...../测试/一级目录/二级目录/.travis(2).yml 总进度 : 4/4 进度 : [ ================================================== ] 100%% 公共数据下载完成!共下载文件5个 个人文件 : 测试.docx 保存路径 : /Users/xxx .....iYunData/个人/系统管理员/测试/测试.docx 总进度 : 0/6 进度 : [ ================================================== ] 100%% 个人文件 : pm2(1) 保存路径 : /Users/xxx .....Data/个人/系统管理员/测试/一级目录/pm2(1) 总进度 : 1/6 进度 : [ ================================================== ] 100%% 个人文件 : package(1).json 保存路径 : /Users/xxx .....统管理员/测试/一级目录/package(1).json 总进度 : 2/6 进度 : [ ================================================== ] 100%% 个人文件 : package(2).json 保存路径 : /Users/xxx .....统管理员/测试/一级目录/package(2).json 总进度 : 3/6 进度 : [ ================================================== ] 100%% 个人文件 : Worker.js 保存路径 : /Users/xxx .....系统管理员/测试/一级目录/二级目录/Worker.js 总进度 : 4/6 进度 : [ ================================================== ] 100%% 个人文件 : which.js 保存路径 : /Users/xxx ...../系统管理员/测试/一级目录/二级目录/which.js 总进度 : 5/6 进度 : [ ================================================== ] 100%% 个人文件 : xdg-open 保存路径 : /Users/xxx ...../系统管理员/测试/一级目录/二级目录/xdg-open 总进度 : 6/6 进度 : [ ================================================== ] 100%% 个人数据下载完成!共下载文件7个
DMC拉取数据。
数据校验。