ssh2-sftp-client 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -5
- package/package.json +5 -3
- package/src/index.js +194 -106
- package/src/utils.js +10 -0
- package/test/append.js +112 -0
- package/test/checksum-tests.js +222 -0
- package/test/fastget.js +103 -0
- package/test/fastput.js +99 -0
- package/test/get.js +104 -0
- package/test/hooks/append-hooks.js +50 -0
- package/test/hooks/checksum-hooks.js +24 -0
- package/test/hooks/chmod-hooks.js +24 -0
- package/test/hooks/delete-hooks.js +27 -0
- package/test/hooks/exist-hooks.js +39 -0
- package/test/hooks/fastGet-hooks.js +63 -0
- package/test/hooks/fastPut-hooks.js +33 -0
- package/test/hooks/get-hooks.js +52 -0
- package/test/hooks/global-hooks.js +66 -0
- package/test/hooks/list-hooks.js +63 -0
- package/test/hooks/mkdir-hooks.js +18 -0
- package/test/hooks/put-hooks.js +24 -0
- package/test/hooks/rename-hooks.js +36 -0
- package/test/hooks/rmdir-hooks.js +34 -0
- package/test/hooks/stat-hooks.js +33 -0
- package/test/mocha.opts +1 -1
- package/test/put.js +114 -0
- package/test/testData/test-file1.txt +39999 -54
- package/test/testData/test-file2.txt.gz +0 -0
- package/test/utility-methods.js +363 -0
- package/test/index.js +0 -686
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
## SSH2 SFTP Client
|
|
2
2
|
a SFTP client for node.js, a wrapper for [ssh2](https://github.com/mscdex/ssh2)
|
|
3
3
|
|
|
4
|
+
Additional documentation on the methods and available options can be found in
|
|
5
|
+
the [ssh2](https://github.com/mscdex/ssh2) and
|
|
6
|
+
[ssh2-streams](https://github.com/mscdex/ssh2-streams) documentation.
|
|
7
|
+
|
|
4
8
|
### Installation
|
|
5
9
|
```shell
|
|
6
10
|
npm install ssh2-sftp-client
|
|
@@ -24,11 +28,57 @@ sftp.connect({
|
|
|
24
28
|
});
|
|
25
29
|
```
|
|
26
30
|
|
|
31
|
+
### Breaking Changes
|
|
32
|
+
|
|
33
|
+
Due to some incompatibilities with stream handling which breaks this module when
|
|
34
|
+
used with Node 10.x, some changes have been implemented that should enhance the
|
|
35
|
+
interface, but which also break compatibility with previous versions.
|
|
36
|
+
|
|
37
|
+
#### Option Changes
|
|
38
|
+
|
|
39
|
+
- The default encoding is null not utf8 as it was previously. This is consistent
|
|
40
|
+
with the defaults for the underlying SSH2 module.
|
|
41
|
+
- The usedCompressed option has been removed. None of the shh2-steams methods
|
|
42
|
+
actually support this option. The 'compress' option can be set as part of the
|
|
43
|
+
connection options. See [ssh2 client event](https://github.com/mscdex/ssh2#user-content-client-methods).
|
|
44
|
+
- The separate explicit option arguments for encoding and useCompression for some methods
|
|
45
|
+
have been replaced with a single 'options' argument, which is an object that
|
|
46
|
+
can have the following properties (defaults shown). See the
|
|
47
|
+
[ssh2-streams](https://github.com/mscdex/ssh2-streams) documentation for an
|
|
48
|
+
explination of the opt8ons.
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
const defaults = {
|
|
52
|
+
highWaterMark: 32 * 1024,
|
|
53
|
+
debug: undefined,
|
|
54
|
+
concurrency: 64,
|
|
55
|
+
chunkSize: 32768,
|
|
56
|
+
step: undefined,
|
|
57
|
+
mode: 0o666,
|
|
58
|
+
autoClose: true,
|
|
59
|
+
encoding: null
|
|
60
|
+
};
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
#### Method Changes
|
|
64
|
+
|
|
65
|
+
#### get(srcPath, dst, options)
|
|
66
|
+
|
|
67
|
+
Used to retrieve a file from a remote SFTP server.
|
|
68
|
+
|
|
69
|
+
- srcPath: path to the file on the remote server
|
|
70
|
+
- dst: Either a string, which will be used as the path to store the file on the
|
|
71
|
+
local system or a writable stream, which will be used as the destination for a
|
|
72
|
+
stream pipe. If undefined, the remote file will be read into a Buffer and
|
|
73
|
+
the buffer returned.
|
|
74
|
+
- options: Options for the get operation e.g. encoding.
|
|
75
|
+
|
|
27
76
|
### Documentation
|
|
28
77
|
the connection to server config pls see [ssh2 client event](https://github.com/mscdex/ssh2#user-content-client-methods).
|
|
29
78
|
|
|
30
79
|
list of methods:
|
|
31
80
|
all the methods will return a Promise;
|
|
81
|
+
|
|
32
82
|
#### List
|
|
33
83
|
Retrieves a directory listing.
|
|
34
84
|
|
|
@@ -54,10 +104,10 @@ group: // group ID
|
|
|
54
104
|
```
|
|
55
105
|
|
|
56
106
|
#### Get
|
|
57
|
-
Get a
|
|
107
|
+
Get a `ReadableStream` from remotePath. The encoding is passed to Node Stream (https://nodejs.org/api/stream.html) and it controls how the content is encoded. For example, when downloading binary data, 'null' should be passed (check node stream documentation). Default to 'null'.
|
|
58
108
|
|
|
59
109
|
```javascript
|
|
60
|
-
sftp.get(remoteFilePath, [
|
|
110
|
+
sftp.get(remoteFilePath, [options]);
|
|
61
111
|
```
|
|
62
112
|
|
|
63
113
|
#### FastGet
|
|
@@ -71,9 +121,9 @@ sftp.fastGet(remotePath, localPath, [options]);
|
|
|
71
121
|
upload a file from `localPath` or `Buffer`, `Stream` data to `remoteFilePath`.The encoding is passed to Node Stream to control how the content is encoded. Default to 'utf8'.
|
|
72
122
|
|
|
73
123
|
```javascript
|
|
74
|
-
sftp.put(localFilePath, remoteFilePath, [
|
|
75
|
-
sftp.put(Buffer, remoteFilePath, [
|
|
76
|
-
sftp.put(Stream, remoteFilePath, [
|
|
124
|
+
sftp.put(localFilePath, remoteFilePath, [optons]);
|
|
125
|
+
sftp.put(Buffer, remoteFilePath, [options]);
|
|
126
|
+
sftp.put(Stream, remoteFilePath, [options]);
|
|
77
127
|
```
|
|
78
128
|
|
|
79
129
|
#### FastPut
|
|
@@ -157,7 +207,21 @@ sftp.on('error', callbackFn)
|
|
|
157
207
|
### FAQ
|
|
158
208
|
|
|
159
209
|
### Log
|
|
210
|
+
### V2.4.3
|
|
211
|
+
- merge #108, #110
|
|
212
|
+
- fix connect promise if connection ends
|
|
213
|
+
|
|
214
|
+
### V2.4.2
|
|
215
|
+
- merge #105
|
|
216
|
+
- fix windows path
|
|
217
|
+
|
|
218
|
+
### V2.4.1
|
|
219
|
+
- merge pr #99, #100
|
|
220
|
+
- bug fix
|
|
221
|
+
|
|
160
222
|
#### V2.4.0
|
|
223
|
+
Requires node.js v7.5.0 or above.
|
|
224
|
+
|
|
161
225
|
- merge pr #97, thanks for @theophilusx
|
|
162
226
|
- Remove emmitter.maxListener warnings
|
|
163
227
|
- Upgraded ssh2 dependency from 0.5.5 to 0.6.1
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ssh2-sftp-client",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "ssh2 sftp client for node",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -12,18 +12,20 @@
|
|
|
12
12
|
"nodejs"
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
|
-
"test": "mocha
|
|
15
|
+
"test": "mocha"
|
|
16
16
|
},
|
|
17
17
|
"author": "见见",
|
|
18
18
|
"email": "jyu213@gmail.com",
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"
|
|
21
|
+
"concat-stream": "^2.0.0",
|
|
22
|
+
"ssh2": "^0.8.2"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"chai": "^4.2.0",
|
|
25
26
|
"chai-as-promised": "^7.1.1",
|
|
26
27
|
"chai-subset": "^1.6.0",
|
|
28
|
+
"checksum": "^0.1.1",
|
|
27
29
|
"dotenv": "^6.1.0",
|
|
28
30
|
"mocha": "^5.2.0"
|
|
29
31
|
}
|
package/src/index.js
CHANGED
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
7
|
const Client = require('ssh2').Client;
|
|
8
|
-
const osPath = require('path');
|
|
8
|
+
const osPath = require('path').posix;
|
|
9
|
+
const utils = require('./utils');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const concat = require('concat-stream');
|
|
9
12
|
|
|
10
|
-
let SftpClient = function(){
|
|
13
|
+
let SftpClient = function() {
|
|
11
14
|
this.client = new Client();
|
|
12
15
|
};
|
|
13
16
|
|
|
@@ -42,7 +45,7 @@ SftpClient.prototype.list = function(path) {
|
|
|
42
45
|
accessTime: item.attrs.atime * 1000,
|
|
43
46
|
rights: {
|
|
44
47
|
user: item.longname.substr(1, 3).replace(reg, ''),
|
|
45
|
-
group: item.longname.substr(4,3).replace(reg, ''),
|
|
48
|
+
group: item.longname.substr(4, 3).replace(reg, ''),
|
|
46
49
|
other: item.longname.substr(7, 3).replace(reg, '')
|
|
47
50
|
},
|
|
48
51
|
owner: item.attrs.uid,
|
|
@@ -81,10 +84,14 @@ SftpClient.prototype.exists = function(path) {
|
|
|
81
84
|
if (err.code === 2) {
|
|
82
85
|
resolve(false);
|
|
83
86
|
} else {
|
|
84
|
-
reject(
|
|
87
|
+
reject(
|
|
88
|
+
new Error(`Error listing ${dir}: code: ${err.code} ${err.message}`)
|
|
89
|
+
);
|
|
85
90
|
}
|
|
86
91
|
} else {
|
|
87
|
-
let [type] = list
|
|
92
|
+
let [type] = list
|
|
93
|
+
.filter(item => item.filename === base)
|
|
94
|
+
.map(item => item.longname.substr(0, 1));
|
|
88
95
|
if (type) {
|
|
89
96
|
resolve(type);
|
|
90
97
|
} else {
|
|
@@ -107,18 +114,18 @@ SftpClient.prototype.stat = function(remotePath) {
|
|
|
107
114
|
let sftp = this.sftp;
|
|
108
115
|
|
|
109
116
|
if (!sftp) {
|
|
110
|
-
return reject(Error('sftp connect error'));
|
|
117
|
+
return reject(Error('sftp connect error'));
|
|
111
118
|
}
|
|
112
|
-
sftp.stat(remotePath, function
|
|
113
|
-
if (err){
|
|
119
|
+
sftp.stat(remotePath, function(err, stats) {
|
|
120
|
+
if (err) {
|
|
114
121
|
reject(new Error(`Failed to stat ${remotePath}: ${err.message}`));
|
|
115
122
|
} else {
|
|
116
|
-
|
|
123
|
+
// format similarly to sftp.list
|
|
117
124
|
resolve({
|
|
118
125
|
mode: stats.mode,
|
|
119
126
|
permissions: stats.permissions,
|
|
120
127
|
owner: stats.uid,
|
|
121
|
-
group: stats.
|
|
128
|
+
group: stats.gid,
|
|
122
129
|
size: stats.size,
|
|
123
130
|
accessTime: stats.atime * 1000,
|
|
124
131
|
modifyTime: stats.mtime * 1000
|
|
@@ -132,34 +139,53 @@ SftpClient.prototype.stat = function(remotePath) {
|
|
|
132
139
|
/**
|
|
133
140
|
* get file
|
|
134
141
|
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* @
|
|
142
|
+
* If a dst argument is provided, it must be either a string, representing the
|
|
143
|
+
* local path to where the data will be put, a stream, in which case data is
|
|
144
|
+
* piped into the stream or undefined, in which case the data is returned as
|
|
145
|
+
* a Buffer object.
|
|
146
|
+
*
|
|
147
|
+
* @param {String} path, remote file path
|
|
148
|
+
* @param {string|stream|undefined} dst, data destination
|
|
149
|
+
* @param {Object} userOptions, options passed to get
|
|
150
|
+
*
|
|
151
|
+
* @return {Promise}
|
|
141
152
|
*/
|
|
142
|
-
SftpClient.prototype.get = function(path,
|
|
143
|
-
let options = this.getOptions(useCompression, encoding, otherOptions);
|
|
144
|
-
|
|
153
|
+
SftpClient.prototype.get = function(path, dst, options) {
|
|
145
154
|
return new Promise((resolve, reject) => {
|
|
146
155
|
let sftp = this.sftp;
|
|
147
156
|
|
|
148
157
|
if (sftp) {
|
|
149
158
|
try {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
stream.on('error', (err) => {
|
|
155
|
-
this.client.removeListener('error', reject);
|
|
156
|
-
return reject(new Error(`Failed get for ${path}: ${err.message}`));
|
|
157
|
-
});
|
|
158
|
-
stream.on('readable', () => {
|
|
159
|
-
this.client.removeListener('error', reject);
|
|
160
|
-
return resolve(stream);
|
|
159
|
+
let rdr = sftp.createReadStream(path, options);
|
|
160
|
+
|
|
161
|
+
rdr.on('error', err => {
|
|
162
|
+
return reject(new Error(`Failed to get ${path}: ${err.message}`));
|
|
161
163
|
});
|
|
162
|
-
|
|
164
|
+
|
|
165
|
+
if (dst === undefined) {
|
|
166
|
+
// no dst specified, return buffer of data
|
|
167
|
+
let concatStream = concat(buff => {
|
|
168
|
+
return resolve(buff);
|
|
169
|
+
});
|
|
170
|
+
rdr.pipe(concatStream);
|
|
171
|
+
} else if (typeof dst === 'string') {
|
|
172
|
+
// dst local file path
|
|
173
|
+
let wtr = fs.createWriteStream(dst);
|
|
174
|
+
wtr.on('error', err => {
|
|
175
|
+
return reject(new Error(`Failed get for ${path}: ${err.message}`));
|
|
176
|
+
});
|
|
177
|
+
wtr.on('finish', () => {
|
|
178
|
+
return resolve(dst);
|
|
179
|
+
});
|
|
180
|
+
rdr.pipe(wtr);
|
|
181
|
+
} else {
|
|
182
|
+
// assume dst is a writeStream
|
|
183
|
+
dst.on('finish', () => {
|
|
184
|
+
return resolve(dst);
|
|
185
|
+
});
|
|
186
|
+
rdr.pipe(dst);
|
|
187
|
+
}
|
|
188
|
+
} catch (err) {
|
|
163
189
|
this.client.removeListener('error', reject);
|
|
164
190
|
return reject(new Error(`Failed get on ${path}: ${err.message}`));
|
|
165
191
|
}
|
|
@@ -179,15 +205,14 @@ SftpClient.prototype.get = function(path, useCompression, encoding, otherOptions
|
|
|
179
205
|
* @return {Promise} the result of downloading the file
|
|
180
206
|
*/
|
|
181
207
|
SftpClient.prototype.fastGet = function(remotePath, localPath, options) {
|
|
182
|
-
options = options || {concurrency: 64, chunkSize: 32768};
|
|
183
208
|
return new Promise((resolve, reject) => {
|
|
184
209
|
let sftp = this.sftp;
|
|
185
210
|
|
|
186
211
|
if (!sftp) {
|
|
187
|
-
return reject(Error('sftp connect error'));
|
|
212
|
+
return reject(Error('sftp connect error'));
|
|
188
213
|
}
|
|
189
|
-
sftp.fastGet(remotePath, localPath, options, function
|
|
190
|
-
if (err){
|
|
214
|
+
sftp.fastGet(remotePath, localPath, options, function(err) {
|
|
215
|
+
if (err) {
|
|
191
216
|
reject(new Error(`Failed to get ${remotePath}: ${err.message}`));
|
|
192
217
|
}
|
|
193
218
|
resolve(`${remotePath} was successfully download to ${localPath}!`);
|
|
@@ -206,16 +231,19 @@ SftpClient.prototype.fastGet = function(remotePath, localPath, options) {
|
|
|
206
231
|
* @return {Promise} the result of downloading the file
|
|
207
232
|
*/
|
|
208
233
|
SftpClient.prototype.fastPut = function(localPath, remotePath, options) {
|
|
209
|
-
options = options || {};
|
|
210
234
|
return new Promise((resolve, reject) => {
|
|
211
235
|
let sftp = this.sftp;
|
|
212
236
|
|
|
213
237
|
if (!sftp) {
|
|
214
|
-
return reject(new Error('sftp connect error'));
|
|
238
|
+
return reject(new Error('sftp connect error'));
|
|
215
239
|
}
|
|
216
|
-
sftp.fastPut(localPath, remotePath, options, function
|
|
240
|
+
sftp.fastPut(localPath, remotePath, options, function(err) {
|
|
217
241
|
if (err) {
|
|
218
|
-
reject(
|
|
242
|
+
reject(
|
|
243
|
+
new Error(
|
|
244
|
+
`Failed to upload ${localPath} to ${remotePath}: ${err.message}`
|
|
245
|
+
)
|
|
246
|
+
);
|
|
219
247
|
}
|
|
220
248
|
resolve(`${localPath} was successfully uploaded to ${remotePath}!`);
|
|
221
249
|
});
|
|
@@ -223,7 +251,6 @@ SftpClient.prototype.fastPut = function(localPath, remotePath, options) {
|
|
|
223
251
|
});
|
|
224
252
|
};
|
|
225
253
|
|
|
226
|
-
|
|
227
254
|
/**
|
|
228
255
|
* Create file
|
|
229
256
|
*
|
|
@@ -233,17 +260,19 @@ SftpClient.prototype.fastPut = function(localPath, remotePath, options) {
|
|
|
233
260
|
* @param {String} encoding. Encoding for the WriteStream, can be any value supported by node streams.
|
|
234
261
|
* @return {[type]} [description]
|
|
235
262
|
*/
|
|
236
|
-
SftpClient.prototype.put = function(input, remotePath,
|
|
237
|
-
let options = this.getOptions(useCompression, encoding, otherOptions);
|
|
238
|
-
|
|
263
|
+
SftpClient.prototype.put = function(input, remotePath, options) {
|
|
239
264
|
return new Promise((resolve, reject) => {
|
|
240
265
|
let sftp = this.sftp;
|
|
241
266
|
|
|
242
267
|
if (sftp) {
|
|
243
268
|
if (typeof input === 'string') {
|
|
244
|
-
sftp.fastPut(input, remotePath, options,
|
|
269
|
+
sftp.fastPut(input, remotePath, options, err => {
|
|
245
270
|
if (err) {
|
|
246
|
-
return reject(
|
|
271
|
+
return reject(
|
|
272
|
+
new Error(
|
|
273
|
+
`Failed to upload ${input} to ${remotePath}: ${err.message}`
|
|
274
|
+
)
|
|
275
|
+
);
|
|
247
276
|
}
|
|
248
277
|
return resolve(`Uploaded ${input} to ${remotePath}`);
|
|
249
278
|
});
|
|
@@ -252,13 +281,58 @@ SftpClient.prototype.put = function(input, remotePath, useCompression, encoding,
|
|
|
252
281
|
let stream = sftp.createWriteStream(remotePath, options);
|
|
253
282
|
|
|
254
283
|
stream.on('error', err => {
|
|
255
|
-
return reject(
|
|
284
|
+
return reject(
|
|
285
|
+
new Error(
|
|
286
|
+
`Failed to upload data stream to ${remotePath}: ${err.message}`
|
|
287
|
+
)
|
|
288
|
+
);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
stream.on('finish', () => {
|
|
292
|
+
return resolve(`Uploaded data stream to ${remotePath}`);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
if (input instanceof Buffer) {
|
|
296
|
+
stream.end(input);
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
input.pipe(stream);
|
|
300
|
+
} else {
|
|
301
|
+
return reject(Error('sftp connect error'));
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Append to file
|
|
308
|
+
*
|
|
309
|
+
* @param {Buffer|stream} input
|
|
310
|
+
* @param {String} remotePath,
|
|
311
|
+
* @param {Object} options
|
|
312
|
+
* @return {[type]} [description]
|
|
313
|
+
*/
|
|
314
|
+
SftpClient.prototype.append = function(input, remotePath, options) {
|
|
315
|
+
return new Promise((resolve, reject) => {
|
|
316
|
+
let sftp = this.sftp;
|
|
317
|
+
|
|
318
|
+
if (sftp) {
|
|
319
|
+
if (typeof input === 'string') {
|
|
320
|
+
throw new Error('Cannot append a file to another');
|
|
321
|
+
}
|
|
322
|
+
let stream = sftp.createWriteStream(remotePath, options);
|
|
323
|
+
|
|
324
|
+
stream.on('error', err => {
|
|
325
|
+
return reject(
|
|
326
|
+
new Error(
|
|
327
|
+
`Failed to upload data stream to ${remotePath}: ${err.message}`
|
|
328
|
+
)
|
|
329
|
+
);
|
|
256
330
|
});
|
|
257
|
-
|
|
258
|
-
stream.on('
|
|
331
|
+
|
|
332
|
+
stream.on('finish', () => {
|
|
259
333
|
return resolve(`Uploaded data stream to ${remotePath}`);
|
|
260
334
|
});
|
|
261
|
-
|
|
335
|
+
|
|
262
336
|
if (input instanceof Buffer) {
|
|
263
337
|
stream.end(input);
|
|
264
338
|
return false;
|
|
@@ -270,13 +344,20 @@ SftpClient.prototype.put = function(input, remotePath, useCompression, encoding,
|
|
|
270
344
|
});
|
|
271
345
|
};
|
|
272
346
|
|
|
347
|
+
/**
|
|
348
|
+
* @async
|
|
349
|
+
*
|
|
350
|
+
* Make a dirextory on remote server
|
|
351
|
+
*
|
|
352
|
+
* @param {string} path, remote directory path.
|
|
353
|
+
* @param {boolean} recursive, if true, recursively create directories
|
|
354
|
+
* @return {Promise}.
|
|
355
|
+
*/
|
|
273
356
|
SftpClient.prototype.mkdir = function(path, recursive = false) {
|
|
274
357
|
let sftp = this.sftp;
|
|
275
358
|
|
|
276
359
|
let doMkdir = p => {
|
|
277
360
|
return new Promise((resolve, reject) => {
|
|
278
|
-
|
|
279
|
-
|
|
280
361
|
if (!sftp) {
|
|
281
362
|
return reject(new Error('sftp connect error'));
|
|
282
363
|
}
|
|
@@ -293,27 +374,35 @@ SftpClient.prototype.mkdir = function(path, recursive = false) {
|
|
|
293
374
|
if (!recursive) {
|
|
294
375
|
return doMkdir(path);
|
|
295
376
|
}
|
|
296
|
-
let mkdir =
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
377
|
+
let mkdir = p => {
|
|
378
|
+
let {dir} = osPath.parse(p);
|
|
379
|
+
return this.exists(dir)
|
|
380
|
+
.then(type => {
|
|
381
|
+
if (!type) {
|
|
382
|
+
return mkdir(dir);
|
|
383
|
+
}
|
|
384
|
+
})
|
|
385
|
+
.then(() => {
|
|
386
|
+
return doMkdir(p);
|
|
387
|
+
});
|
|
307
388
|
};
|
|
308
389
|
return mkdir(path);
|
|
309
390
|
};
|
|
310
391
|
|
|
392
|
+
/**
|
|
393
|
+
* @async
|
|
394
|
+
*
|
|
395
|
+
* Remove directory on remote server
|
|
396
|
+
*
|
|
397
|
+
* @param {string} path, path to directory to be removed
|
|
398
|
+
* @param {boolean} recursive, if true, remove direcories/files in target
|
|
399
|
+
* @return {Promise}..
|
|
400
|
+
*/
|
|
311
401
|
SftpClient.prototype.rmdir = function(path, recursive = false) {
|
|
312
402
|
let sftp = this.sftp;
|
|
313
403
|
|
|
314
404
|
let doRmdir = p => {
|
|
315
405
|
return new Promise((resolve, reject) => {
|
|
316
|
-
|
|
317
406
|
if (!sftp) {
|
|
318
407
|
return reject(new Error('sftp connect error'));
|
|
319
408
|
}
|
|
@@ -331,21 +420,27 @@ SftpClient.prototype.rmdir = function(path, recursive = false) {
|
|
|
331
420
|
return doRmdir(path);
|
|
332
421
|
}
|
|
333
422
|
|
|
334
|
-
let rmdir =
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
423
|
+
let rmdir = p => {
|
|
424
|
+
let list;
|
|
425
|
+
let files;
|
|
426
|
+
let dirs;
|
|
427
|
+
return this.list(p)
|
|
428
|
+
.then(res => {
|
|
429
|
+
list = res;
|
|
430
|
+
files = list.filter(item => item.type === '-');
|
|
431
|
+
dirs = list.filter(item => item.type === 'd');
|
|
432
|
+
return utils.forEachAsync(files, f => {
|
|
433
|
+
return this.delete(osPath.join(p, f.name));
|
|
434
|
+
});
|
|
435
|
+
})
|
|
436
|
+
.then(() => {
|
|
437
|
+
return utils.forEachAsync(dirs, d => {
|
|
438
|
+
return rmdir(osPath.join(p, d.name));
|
|
439
|
+
});
|
|
440
|
+
})
|
|
441
|
+
.then(() => {
|
|
442
|
+
return doRmdir(p);
|
|
443
|
+
});
|
|
349
444
|
};
|
|
350
445
|
return rmdir(path);
|
|
351
446
|
};
|
|
@@ -357,16 +452,16 @@ SftpClient.prototype.rmdir = function(path, recursive = false) {
|
|
|
357
452
|
*
|
|
358
453
|
* @param {string} path - path to the file to delete
|
|
359
454
|
* @return {Promise} with string 'Successfully deleeted file' once resolved
|
|
360
|
-
*
|
|
455
|
+
*
|
|
361
456
|
*/
|
|
362
457
|
SftpClient.prototype.delete = function(path) {
|
|
363
458
|
return new Promise((resolve, reject) => {
|
|
364
459
|
let sftp = this.sftp;
|
|
365
460
|
|
|
366
461
|
if (!sftp) {
|
|
367
|
-
return reject(new Error('sftp connect error'));
|
|
462
|
+
return reject(new Error('sftp connect error'));
|
|
368
463
|
}
|
|
369
|
-
sftp.unlink(path,
|
|
464
|
+
sftp.unlink(path, err => {
|
|
370
465
|
if (err) {
|
|
371
466
|
reject(new Error(`Failed to delete file ${path}: ${err.message}`));
|
|
372
467
|
}
|
|
@@ -385,18 +480,22 @@ SftpClient.prototype.delete = function(path) {
|
|
|
385
480
|
* @param {string} remotePath - path to the new name.
|
|
386
481
|
*
|
|
387
482
|
* @return {Promise}
|
|
388
|
-
*
|
|
483
|
+
*
|
|
389
484
|
*/
|
|
390
485
|
SftpClient.prototype.rename = function(srcPath, remotePath) {
|
|
391
486
|
return new Promise((resolve, reject) => {
|
|
392
487
|
let sftp = this.sftp;
|
|
393
488
|
|
|
394
489
|
if (!sftp) {
|
|
395
|
-
return reject(new Error('sftp connect error'));
|
|
490
|
+
return reject(new Error('sftp connect error'));
|
|
396
491
|
}
|
|
397
|
-
sftp.rename(srcPath, remotePath,
|
|
492
|
+
sftp.rename(srcPath, remotePath, err => {
|
|
398
493
|
if (err) {
|
|
399
|
-
reject(
|
|
494
|
+
reject(
|
|
495
|
+
new Error(
|
|
496
|
+
`Failed to rename file ${srcPath} to ${remotePath}: ${err.message}`
|
|
497
|
+
)
|
|
498
|
+
);
|
|
400
499
|
}
|
|
401
500
|
resolve(`Successfully renamed ${srcPath} to ${remotePath}`);
|
|
402
501
|
});
|
|
@@ -419,11 +518,13 @@ SftpClient.prototype.chmod = function(remotePath, mode) {
|
|
|
419
518
|
let sftp = this.sftp;
|
|
420
519
|
|
|
421
520
|
if (!sftp) {
|
|
422
|
-
return reject(new Error('sftp connect error'));
|
|
521
|
+
return reject(new Error('sftp connect error'));
|
|
423
522
|
}
|
|
424
|
-
sftp.chmod(remotePath, mode,
|
|
523
|
+
sftp.chmod(remotePath, mode, err => {
|
|
425
524
|
if (err) {
|
|
426
|
-
reject(
|
|
525
|
+
reject(
|
|
526
|
+
new Error(`Failed to change mode for ${remotePath}: ${err.message}`)
|
|
527
|
+
);
|
|
427
528
|
}
|
|
428
529
|
resolve('Successfully change file mode');
|
|
429
530
|
});
|
|
@@ -440,7 +541,7 @@ SftpClient.prototype.chmod = function(remotePath, mode) {
|
|
|
440
541
|
* @param {string} connectMethod - ???
|
|
441
542
|
*
|
|
442
543
|
* @return {Promise} which will resolve to an sftp client object
|
|
443
|
-
*
|
|
544
|
+
*
|
|
444
545
|
*/
|
|
445
546
|
SftpClient.prototype.connect = function(config, connectMethod) {
|
|
446
547
|
connectMethod = connectMethod || 'on';
|
|
@@ -449,6 +550,7 @@ SftpClient.prototype.connect = function(config, connectMethod) {
|
|
|
449
550
|
this.client[connectMethod]('ready', () => {
|
|
450
551
|
this.client.sftp((err, sftp) => {
|
|
451
552
|
this.client.removeListener('error', reject);
|
|
553
|
+
this.client.removeListener('end', reject);
|
|
452
554
|
if (err) {
|
|
453
555
|
reject(new Error(`Failed to connect to server: ${err.message}`));
|
|
454
556
|
}
|
|
@@ -456,6 +558,7 @@ SftpClient.prototype.connect = function(config, connectMethod) {
|
|
|
456
558
|
resolve(sftp);
|
|
457
559
|
});
|
|
458
560
|
})
|
|
561
|
+
.on('end', reject)
|
|
459
562
|
.on('error', reject)
|
|
460
563
|
.connect(config);
|
|
461
564
|
});
|
|
@@ -465,33 +568,18 @@ SftpClient.prototype.connect = function(config, connectMethod) {
|
|
|
465
568
|
* @async
|
|
466
569
|
*
|
|
467
570
|
* Close the SFTP connection
|
|
468
|
-
*
|
|
571
|
+
*
|
|
469
572
|
*/
|
|
470
573
|
SftpClient.prototype.end = function() {
|
|
471
|
-
return new Promise(
|
|
574
|
+
return new Promise(resolve => {
|
|
472
575
|
this.client.end();
|
|
473
576
|
resolve();
|
|
474
577
|
});
|
|
475
578
|
};
|
|
476
579
|
|
|
477
|
-
SftpClient.prototype.getOptions = function(useCompression, encoding, otherOptions) {
|
|
478
|
-
if(encoding === undefined){
|
|
479
|
-
encoding = 'utf8';
|
|
480
|
-
}
|
|
481
|
-
let options = Object.assign({}, otherOptions || {}, {encoding: encoding}, useCompression);
|
|
482
|
-
return options;
|
|
483
|
-
};
|
|
484
|
-
|
|
485
580
|
// add Event type support
|
|
486
581
|
SftpClient.prototype.on = function(eventType, callback) {
|
|
487
582
|
this.client.on(eventType, callback);
|
|
488
583
|
};
|
|
489
584
|
|
|
490
|
-
|
|
491
585
|
module.exports = SftpClient;
|
|
492
|
-
|
|
493
|
-
// sftp = new SftpClient()
|
|
494
|
-
// sftp.client.on('event')
|
|
495
|
-
//
|
|
496
|
-
// sftp.on('end', ()=>{}) => this.client.on('event', callback)
|
|
497
|
-
// sftp.on('error', () => {})
|