1 |
|
2 |
|
3 |
|
4 | const 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 |
|
11 | class 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 |
|
49 |
|
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 |
|
91 |
|
92 |
|
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 |
|
133 | module.exports = Downloader;
|