UNPKG

4.37 kBJavaScriptView Raw
1/**
2 * Created by Rodey on 2018/3/23.
3 */
4const util = require('../utils'),
5 extend = require('extend'),
6 SSH2 = require('ssh2').Client,
7 EventEmitter = require('events').EventEmitter,
8 LoadingORA = require('../loadProgress').LoadingORA,
9 T = require('../tools');
10
11class Downloader extends EventEmitter {
12 constructor(fromPath, toPath, auths, filters) {
13 super();
14
15 this.fromPath = fromPath;
16 this.toPath = toPath;
17 this.auths = auths;
18 this.ssh2 = null;
19 this.sftp = null;
20 this.filters = filters || [];
21 this.count = 0;
22 }
23
24 start() {
25 this.initSSH();
26 }
27
28 initSSH() {
29 if (!this.ssh2) {
30 this.ssh2 = new SSH2();
31 }
32 this.ssh2.on('ready', this._onReady.bind(this));
33 this.ssh2.connect(this.auths);
34 }
35
36 download() {
37 // 拉取远程文件到本地
38 this.ssh2.sftp((err, sftp) => {
39 if (err) throw err;
40 this.sftp = sftp;
41 this.emit('start', { message: '→ Download start ...... ', backup: this });
42 this._readdir(this.fromPath, null, sftp);
43 });
44 }
45
46 /**
47 * 递归读取远程目录
48 * @param String dir 远程目录
49 * @param String out 本地目录
50 */
51 _readdir(dir, out) {
52 this.count++;
53 this.sftp.readdir(dir, (err, list) => {
54 if (err) {
55 if (err.message !== 'No such file') {
56 this._error(err.message, 500, dir);
57 } else {
58 this._error(`⚠ No such '${dir}'`, 404, dir);
59 }
60 return false;
61 }
62
63 if (list && list.length > 0) {
64 for (let i = 0; i < list.length; ++i) {
65 const item = list[i];
66 const input = T.Path.posix.join(dir, item.filename);
67 const output = T.Path.resolve(out || this.toPath, item.filename);
68 const stat = item.attrs;
69 // 判断是否在过滤中
70 if (this.filters.indexOf(input) > -1) continue;
71 // 目录
72 if (stat.isDirectory()) {
73 // 判断本地是否有此目录
74 !T.fs.existsSync(output) && T.fs.mkdirSync(output);
75 // 循环读取
76 this._readdir(input, output);
77 } else if (stat.isFile(input)) {
78 this._getFile(input, output, stat);
79 }
80 }
81 } else {
82 this._error(`⚠ '${dir}' is empty directory`, 400, dir);
83 }
84 this.count--;
85 });
86 }
87
88 /**
89 * 拉取文件
90 * @param String input 输入路径
91 * @param String output 输出路径
92 * @param Stat stat Stat对象 (https://github.com/mscdex/ssh2-streams/blob/master/SFTPStream.md#attrs)
93 */
94 _getFile(input, output, stat) {
95 this.count++;
96 let readStream = this.sftp.createReadStream(input, {
97 flags: 'r',
98 encoding: null,
99 handle: null,
100 mode: 0o666,
101 autoClose: true
102 });
103 let writeStream = T.fs.createWriteStream(output);
104 readStream.pipe(writeStream).on('finish', () => {
105 const message = `√ [${T.getTime()}] download to '${output}', after ${T.msg.yellow(stat.size / 1000 + ' kb')}`;
106 this.emit('file_downloaded', { output, size: stat.size, message });
107 this._finishEnd(--this.count);
108 });
109 readStream.on('err', err => {
110 T.log.error(err.message);
111 });
112 }
113
114 // 备份完成
115 _finishEnd(count) {
116 count === 0 && this._done('√ Download finish');
117 }
118
119 _done(message, state, directory) {
120 this.emit('done', { message: message, state: state || 200, directory, backup: this });
121 }
122
123 _error(message, state, directory) {
124 this.emit('error', { message, state, directory, backup: this });
125 directory === this.fromPath && this._done(message, state, directory);
126 }
127
128 _onReady() {
129 this.download();
130 }
131}
132
133module.exports = Downloader;