UNPKG

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