ssh2-sftp-client 6.0.1 → 7.0.3
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 +126 -118
- package/README.org +177 -153
- package/package.json +9 -8
- package/src/index.js +508 -374
- package/src/utils.js +171 -39
- package/CHANGELOG.org +0 -225
package/README.md
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
- [Overview](#sec-1)
|
|
2
2
|
- [Installation](#sec-2)
|
|
3
3
|
- [Basic Usage](#sec-3)
|
|
4
|
-
- [Version
|
|
5
|
-
- [Version 6.0.1](#sec-4-1)
|
|
6
|
-
- [Version 6.0.0 Changes](#sec-4-2)
|
|
4
|
+
- [Version 7.x Changes](#sec-4)
|
|
7
5
|
- [Documentation](#sec-5)
|
|
8
6
|
- [Specifying Paths](#sec-5-1)
|
|
9
7
|
- [Methods](#sec-5-2)
|
|
10
8
|
- [new SftpClient(name) ===> SFTP client object](#sec-5-2-1)
|
|
11
|
-
- [connect(config) ===>
|
|
9
|
+
- [connect(config) ===> SFTP object](#sec-5-2-2)
|
|
12
10
|
- [list(path, pattern) ==> Array[object]](#sec-5-2-3)
|
|
13
11
|
- [exists(path) ==> boolean](#sec-5-2-4)
|
|
14
12
|
- [stat(path) ==> object](#sec-5-2-5)
|
|
@@ -31,7 +29,8 @@
|
|
|
31
29
|
- [Add and Remove Listeners](#sec-5-2-22)
|
|
32
30
|
- [Platform Quirks & Warnings](#sec-6)
|
|
33
31
|
- [Server Capabilities](#sec-6-1)
|
|
34
|
-
- [Promises &
|
|
32
|
+
- [Promises, Events & Managing Exceptions](#sec-6-2)
|
|
33
|
+
- [Adding Custom Handlers](#sec-6-2-1)
|
|
35
34
|
- [Windows Based Servers](#sec-6-3)
|
|
36
35
|
- [Don't Re-use SftpClient Objects](#sec-6-4)
|
|
37
36
|
- [FAQ](#sec-7)
|
|
@@ -41,6 +40,7 @@
|
|
|
41
40
|
- [How can I connect through a Socks Proxy](#sec-7-4)
|
|
42
41
|
- [Timeout while waiting for handshake or handshake errors](#sec-7-5)
|
|
43
42
|
- [How can I limit upload/download speed](#sec-7-6)
|
|
43
|
+
- [Connection hangs or fails for larger files](#sec-7-7)
|
|
44
44
|
- [Examples](#sec-8)
|
|
45
45
|
- [Troubleshooting](#sec-9)
|
|
46
46
|
- [Common Errors](#sec-9-1)
|
|
@@ -59,24 +59,14 @@
|
|
|
59
59
|
|
|
60
60
|
an SFTP client for node.js, a wrapper around [SSH2](https://github.com/mscdex/ssh2) which provides a high level convenience abstraction as well as a Promise based API.
|
|
61
61
|
|
|
62
|
-
Documentation on the methods and available options in the underlying modules can be found on the [SSH2](https://github.com/mscdex/ssh2)
|
|
62
|
+
Documentation on the methods and available options in the underlying modules can be found on the [SSH2](https://github.com/mscdex/ssh2) project pages.
|
|
63
63
|
|
|
64
|
-
Current stable release is **
|
|
64
|
+
Current stable release is **v7.0.3**.
|
|
65
65
|
|
|
66
|
-
Code has been tested against Node versions 12.
|
|
66
|
+
Code has been tested against Node versions 12.22.1, 14.17.0 and 16.2.0
|
|
67
67
|
|
|
68
68
|
Node versions < 10.x are not supported.
|
|
69
69
|
|
|
70
|
-
<span class="underline">WARNING</span> There is currently a regression error with versions of node later than version 14.0. It appears that when using streams with chunk sizes which exceed the high water mark for the stream, a drain event is no longer emitted. As a result, streams with sufficient data will hang indefinitely. This appears to affect fastput, fastget, put and possibly get operations. Until this issue is resolved and a new version of ssh2/ssh2-streams is released, using node v14 is not recommended.
|
|
71
|
-
|
|
72
|
-
A bug report hass been logged against the ssh2-streams library as [issue 156](https://github.com/mscdex/ssh2-streams/issues/156).
|
|
73
|
-
|
|
74
|
-
<span class="underline">UPDATE</span>: The author of the upstream ssh2 and ssh2-streams module has decided on a re-write of the ssh2 module to address the above issues as well as some other design limitations and to allow the module to better fit in with newer versions of node. As part of that process, the functionality of ssh2-streams is being incorporated into the main ssh2 module and the ssh2-streams module is being deprecated. This will require a significant update to this module and may result in some API changes, depending on what changes in the re-write of ssh2.
|
|
75
|
-
|
|
76
|
-
To support these changes, a new branch called *version-6* has been created. This branch will use the newest version of ssh2 and for now is very much experimental and subject to change.
|
|
77
|
-
|
|
78
|
-
<span class="underline">UPDATE</span>: Apparently the change in core node which cause the issue with ssh2 has been rolled back in node version 15.3.0. Testing seems to indicate the above issue does not exist in that version of node.
|
|
79
|
-
|
|
80
70
|
# Installation<a id="sec-2"></a>
|
|
81
71
|
|
|
82
72
|
```shell
|
|
@@ -103,23 +93,13 @@ sftp.connect({
|
|
|
103
93
|
});
|
|
104
94
|
```
|
|
105
95
|
|
|
106
|
-
# Version
|
|
107
|
-
|
|
108
|
-
## Version 6.0.1<a id="sec-4-1"></a>
|
|
96
|
+
# Version 7.x Changes<a id="sec-4"></a>
|
|
109
97
|
|
|
110
|
-
-
|
|
111
|
-
- Add finally clauses to all promises to ensure release of temporary listeners.
|
|
112
|
-
- Add nyc module to improve test coverage
|
|
113
|
-
- Added additional utils tests to improve test coverage
|
|
114
|
-
- Removed some unnecessary util functions to reduce code size
|
|
98
|
+
- This version is based on version 1.x.x of `ssh2`. This version of `ssh2` is a complete re-write of the `ssh2` library. This re-write addresses issues encountered when using node v14 as well as some design weaknesses in the previous 0.8.x version.
|
|
115
99
|
|
|
116
|
-
|
|
100
|
+
- **Breaking Change** Expanded option handling for `get()` and `put()` methods. A number of use cases were identified where setting specific options on the read and write streams and the pipe operation are necessary. For example, disabling `autoClose` on streams or the `end` event in pipes. The `options` argument for `get()` and `put()` calls now supports properties for `readStreamOptions`, `writeStreamOptions` and `pipeOptions`. Note that options are only applied to streams created by the `get()` and `put()` methods. Streams passed into these methods are under the control of the client code and therefore cannot have options supplied in arguments to those streams (you would apply such options when you create the streams). Options are typically only necessary in special use cases. Most of the time, no options are required. However, if you are currently using options to either `put()` or `get()`, you will need to update your code to map these options to the new structure.
|
|
117
101
|
|
|
118
|
-
-
|
|
119
|
-
- Added new filter argument to `uploadDir()` and `downloadDir()` methods. The filter argument is a regular expression used to match the files and directories to be included in the upload or download. Defaults to match all files and directories.
|
|
120
|
-
- New event handling approach. Have standardised the adding and removing of event handlers as well as added event handlers for *close* and *end* events. Some sftp servers appear to abruptly terminate connections without issuing an *error* event. This could result in failing to resolve the promise associated with the action being performed. Adding *close* and *end* listeners to reject a promise should prevent the scripts from hanging when the remote server does not return either a status or an error.
|
|
121
|
-
- Replace the `retry` library module with the `promise-retry` library module. This module also uses the `retry` library, but at a higher level abstracted for use with Promises. As a result, the number of failed retries is no longer returned as part of the error message when all attempted connection retries fail.
|
|
122
|
-
- Reduced argument verification code. Version 5.x added a lot of additional argument verification code in an attempt to provide more meaningful error messages. While this goal was achieved, it did have a significant performance impact, especially with respect to small file transfers. This additional argument verification has now been removed in favour of faster code. The downside is that some error messages have changed and in some cases, will not be as meaningful or will be more generic in nature. This seems as a reasonable compromise. It may result in increased difficulty tracking down why a script is failing, but once a script is working, it should be more efficient. If your code relies on the text in error messages, you will need to verify whether the text is still the same in any testing and adjust where necessary.
|
|
102
|
+
- Improved event handling. A listener for a global error event is now defined to catch errors which occur in-between method calls i.e. connection lost in-between calls to the library methods. A new mechanism has also been added for removal of listeners when no longer required.
|
|
123
103
|
|
|
124
104
|
# Documentation<a id="sec-5"></a>
|
|
125
105
|
|
|
@@ -129,7 +109,7 @@ All the methods will return a Promise, except for `on()` and `removeListener()`,
|
|
|
129
109
|
|
|
130
110
|
## Specifying Paths<a id="sec-5-1"></a>
|
|
131
111
|
|
|
132
|
-
The convention with both FTP and SFTP is that paths are specified using a 'nix' style i.e. use
|
|
112
|
+
The convention with both FTP and SFTP is that paths are specified using a 'nix' style i.e. use `/` as the path separator. This means that even if your SFTP server is running on a win32 platform, you should use `/` instead of `\` as the path separator. For example, for a win32 path of `C:\Users\fred` you would actually use `/C:/Users/fred`. If your win32 server does not support the 'nix' path convention, you can try setting the `remotePathSep` property of the `SftpClient` object to the path separator of your remote server. This **might** work, but has not been tested. Please let me know if you need to do this and provide details of the SFTP server so that I can try to create an appropriate environment and adjust things as necessary. At this point, I'm not aware of any win32 based SFTP servers which do not support the 'nix' path convention.
|
|
133
113
|
|
|
134
114
|
All remote paths must either be absolute e.g. `/absolute/path/to/file` or they can be relative with a prefix of either `./` (relative to current remote directory) or `../` (relative to parent of current remote directory) e.g. `./relative/path/to/file` or `../relative/to/parent/file`. It is also possible to do things like `../../../file` to specify the parent of the parent of the parent of the current remote directory. The shell tilde (`~`) and common environment variables like `$HOME` are NOT supported.
|
|
135
115
|
|
|
@@ -171,17 +151,17 @@ Constructor to create a new `ssh2-sftp-client` object. An optional `name` string
|
|
|
171
151
|
|
|
172
152
|
```javascript
|
|
173
153
|
'use strict';
|
|
174
|
-
|
|
154
|
+
|
|
175
155
|
const Client = require('ssh2-sftp-client');
|
|
176
|
-
|
|
156
|
+
|
|
177
157
|
const config = {
|
|
178
158
|
host: 'example.com',
|
|
179
159
|
username: 'donald',
|
|
180
160
|
password: 'my-secret'
|
|
181
161
|
};
|
|
182
|
-
|
|
162
|
+
|
|
183
163
|
const sftp = new Client('example-client');
|
|
184
|
-
|
|
164
|
+
|
|
185
165
|
sftp.connect(config)
|
|
186
166
|
.then(() => {
|
|
187
167
|
return sftp.cwd();
|
|
@@ -195,19 +175,19 @@ Constructor to create a new `ssh2-sftp-client` object. An optional `name` string
|
|
|
195
175
|
});
|
|
196
176
|
```
|
|
197
177
|
|
|
198
|
-
### connect(config) ===>
|
|
178
|
+
### connect(config) ===> SFTP object<a id="sec-5-2-2"></a>
|
|
199
179
|
|
|
200
180
|
Connect to an sftp server. Full documentation for connection options is available [here](https://github.com/mscdex/ssh2#user-content-client-methods)
|
|
201
181
|
|
|
202
182
|
1. Connection Options
|
|
203
183
|
|
|
204
184
|
This module is based on the excellent [SSH2](https://github.com/mscdex/ssh2#client) module. That module is a general SSH2 client and server library and provides much more functionality than just SFTP connectivity. Many of the connect options provided by that module are less relevant for SFTP connections. It is recommended you keep the config options to the minimum needed and stick to the options listed in the `commonOpts` below.
|
|
205
|
-
|
|
185
|
+
|
|
206
186
|
The `retries`, `retry_factor` and `retry_minTimeout` options are not part of the SSH2 module. These are part of the configuration for the [retry](https://www.npmjs.com/package/retry) package and what is used to enable retrying of sftp connection attempts. See the documentation for that package for an explanation of these values.
|
|
207
|
-
|
|
187
|
+
|
|
208
188
|
```javascript
|
|
209
189
|
// common options
|
|
210
|
-
|
|
190
|
+
|
|
211
191
|
let commonOpts {
|
|
212
192
|
host: 'localhost', // string Hostname or IP of server.
|
|
213
193
|
port: 22, // Port number of the server.
|
|
@@ -226,9 +206,9 @@ Connect to an sftp server. Full documentation for connection options is availabl
|
|
|
226
206
|
retry_factor: 2 // integer. Time factor used to calculate time between retries
|
|
227
207
|
retry_minTimeout: 2000 // integer. Minimum timeout between attempts
|
|
228
208
|
};
|
|
229
|
-
|
|
209
|
+
|
|
230
210
|
// rarely used options
|
|
231
|
-
|
|
211
|
+
|
|
232
212
|
let advancedOpts {
|
|
233
213
|
localAddress,
|
|
234
214
|
localPort,
|
|
@@ -269,16 +249,16 @@ Retrieves a directory listing. This method returns a Promise, which once realise
|
|
|
269
249
|
|
|
270
250
|
```javascript
|
|
271
251
|
const Client = require('ssh2-sftp-client');
|
|
272
|
-
|
|
252
|
+
|
|
273
253
|
const config = {
|
|
274
254
|
host: 'example.com',
|
|
275
255
|
port: 22,
|
|
276
256
|
username: 'red-don',
|
|
277
257
|
password: 'my-secret'
|
|
278
258
|
};
|
|
279
|
-
|
|
259
|
+
|
|
280
260
|
let sftp = new Client;
|
|
281
|
-
|
|
261
|
+
|
|
282
262
|
sftp.connect(config)
|
|
283
263
|
.then(() => {
|
|
284
264
|
return sftp.list('/path/to/remote/dir');
|
|
@@ -297,7 +277,7 @@ Retrieves a directory listing. This method returns a Promise, which once realise
|
|
|
297
277
|
2. Return Objects
|
|
298
278
|
|
|
299
279
|
The objects in the array returned by `list()` have the following properties;
|
|
300
|
-
|
|
280
|
+
|
|
301
281
|
```javascript
|
|
302
282
|
{
|
|
303
283
|
type: // file type(-, d, l)
|
|
@@ -318,11 +298,11 @@ Retrieves a directory listing. This method returns a Promise, which once realise
|
|
|
318
298
|
3. Pattern Filter
|
|
319
299
|
|
|
320
300
|
The filter options can be a regular expression (most powerful option) or a simple *glob*-like string where \* will match any number of characters, e.g.
|
|
321
|
-
|
|
301
|
+
|
|
322
302
|
foo* => foo, foobar, foobaz
|
|
323
303
|
*bar => bar, foobar, tabbar
|
|
324
304
|
*oo* => foo, foobar, look, book
|
|
325
|
-
|
|
305
|
+
|
|
326
306
|
The *glob*-style matching is very simple. In most cases, you are best off using a real regular expression which will allow you to do more powerful matching and anchor matches to the beginning/end of the string etc.
|
|
327
307
|
|
|
328
308
|
### exists(path) ==> boolean<a id="sec-5-2-4"></a>
|
|
@@ -333,16 +313,16 @@ Tests to see if remote file or directory exists. Returns type of remote object i
|
|
|
333
313
|
|
|
334
314
|
```javascript
|
|
335
315
|
const Client = require('ssh2-sftp-client');
|
|
336
|
-
|
|
316
|
+
|
|
337
317
|
const config = {
|
|
338
318
|
host: 'example.com',
|
|
339
319
|
port: 22,
|
|
340
320
|
username: 'red-don',
|
|
341
321
|
password: 'my-secret'
|
|
342
322
|
};
|
|
343
|
-
|
|
323
|
+
|
|
344
324
|
let sftp = new Client;
|
|
345
|
-
|
|
325
|
+
|
|
346
326
|
sftp.connect(config)
|
|
347
327
|
.then(() => {
|
|
348
328
|
return sftp.exists('/path/to/remote/dir');
|
|
@@ -367,7 +347,7 @@ Returns the attributes associated with the object pointed to by `path`.
|
|
|
367
347
|
1. Attributes
|
|
368
348
|
|
|
369
349
|
The `stat()` method returns an object with the following properties;
|
|
370
|
-
|
|
350
|
+
|
|
371
351
|
```javascript
|
|
372
352
|
let stats = {
|
|
373
353
|
mode: 33279, // integer representing type and permissions
|
|
@@ -390,7 +370,7 @@ Returns the attributes associated with the object pointed to by `path`.
|
|
|
390
370
|
|
|
391
371
|
```javascript
|
|
392
372
|
let client = new Client();
|
|
393
|
-
|
|
373
|
+
|
|
394
374
|
client.connect(config)
|
|
395
375
|
.then(() => {
|
|
396
376
|
return client.stat('/path/to/remote/file');
|
|
@@ -418,28 +398,33 @@ In general, if your going to pass in a string as the destination, you are better
|
|
|
418
398
|
|
|
419
399
|
1. Options
|
|
420
400
|
|
|
421
|
-
The options
|
|
422
|
-
|
|
401
|
+
The `options` argument can be used to pass options to the underlying streams and pipe call used by this method. The argument is an object with three possible properties, `readStreamOptions`, `writeStreamOptions` and `pipeOptions`. The values for each of these properties should be an object containing the required options. For example, possible read stream and pipe options could be defined as
|
|
402
|
+
|
|
423
403
|
```javascript
|
|
424
|
-
{
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
404
|
+
let options = {
|
|
405
|
+
readStreamOptions: {
|
|
406
|
+
flags: 'r',
|
|
407
|
+
encoding: null,
|
|
408
|
+
handle: null,
|
|
409
|
+
mode: 0o666,
|
|
410
|
+
autoClose: true
|
|
411
|
+
},
|
|
412
|
+
pipeOptions: {
|
|
413
|
+
end: false
|
|
414
|
+
}};
|
|
415
|
+
|
|
431
416
|
```
|
|
432
|
-
|
|
417
|
+
|
|
433
418
|
Most of the time, you won't want to use any options. Sometimes, it may be useful to set the encoding. For example, to 'utf-8'. However, it is important not to do this for binary files to avoid data corruption.
|
|
434
419
|
|
|
435
420
|
2. Example Use
|
|
436
421
|
|
|
437
422
|
```javascript
|
|
438
423
|
let client = new Client();
|
|
439
|
-
|
|
424
|
+
|
|
440
425
|
let remotePath = '/remote/server/path/file.txt';
|
|
441
426
|
let dst = fs.createWriteStream('/local/file/path/copy.txt');
|
|
442
|
-
|
|
427
|
+
|
|
443
428
|
client.connect(config)
|
|
444
429
|
.then(() => {
|
|
445
430
|
return client.get(remotePath, dst);
|
|
@@ -451,7 +436,7 @@ In general, if your going to pass in a string as the destination, you are better
|
|
|
451
436
|
console.error(err.message);
|
|
452
437
|
});
|
|
453
438
|
```
|
|
454
|
-
|
|
439
|
+
|
|
455
440
|
- **Tip:** See examples file in the Git repository for more examples. You can pass any writeable stream in as the destination. For example, if you pass in `zlib.createGunzip()` writeable stream, you can both download and decompress a gzip file 'on the fly'.
|
|
456
441
|
|
|
457
442
|
### fastGet(remotePath, localPath, options) ===> string<a id="sec-5-2-7"></a>
|
|
@@ -472,7 +457,7 @@ Downloads a file at remotePath to localPath using parallel reads for faster thro
|
|
|
472
457
|
// chunk is transferred
|
|
473
458
|
}
|
|
474
459
|
```
|
|
475
|
-
|
|
460
|
+
|
|
476
461
|
- **Warning:** Some servers do not respond correctly to requests to alter chunk size. This can result in lost or corrupted data.
|
|
477
462
|
|
|
478
463
|
2. Sample Use
|
|
@@ -481,7 +466,7 @@ Downloads a file at remotePath to localPath using parallel reads for faster thro
|
|
|
481
466
|
let client = new Client();
|
|
482
467
|
let remotePath = '/server/path/file.txt';
|
|
483
468
|
let localPath = '/local/path/file.txt';
|
|
484
|
-
|
|
469
|
+
|
|
485
470
|
client.connect(config)
|
|
486
471
|
.then(() => {
|
|
487
472
|
client.fastGet(remotePath, localPath);
|
|
@@ -500,31 +485,32 @@ Upload data from local system to remote server. If the `src` argument is a strin
|
|
|
500
485
|
|
|
501
486
|
- **src:** string | buffer | readable stream. Data source for data to copy to the remote server.
|
|
502
487
|
- **remotePath:** string. Path to the remote file to be created on the server.
|
|
503
|
-
- **options:** object. Options which can be passed to adjust the write stream used in sending the data to the remote server (see below).
|
|
488
|
+
- **options:** object. Options which can be passed to adjust the read and write stream used in sending the data to the remote server or the pipe call used to make the data transfer (see below).
|
|
504
489
|
|
|
505
490
|
1. Options
|
|
506
491
|
|
|
507
|
-
The
|
|
508
|
-
|
|
492
|
+
The options object supports three properties, `readStreamOptions`, `writeStreamOptions` and `pipeOptions`. The value for each property should be an object with options as properties and their associated values representing the option value. For example, you might use the following to set `writeStream` options.
|
|
493
|
+
|
|
509
494
|
```javascript
|
|
510
495
|
{
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
496
|
+
writeStreamOptions: {
|
|
497
|
+
flags: 'w', // w - write and a - append
|
|
498
|
+
encoding: null, // use null for binary files
|
|
499
|
+
mode: 0o666, // mode to use for created file (rwx)
|
|
500
|
+
autoClose: true // automatically close the write stream when finished
|
|
501
|
+
}}
|
|
516
502
|
```
|
|
517
|
-
|
|
503
|
+
|
|
518
504
|
The most common options to use are mode and encoding. The values shown above are the defaults. You do not have to set encoding to utf-8 for text files, null is fine for all file types. However, using utf-8 encoding for binary files will often result in data corruption.
|
|
519
505
|
|
|
520
506
|
2. Example Use
|
|
521
507
|
|
|
522
508
|
```javascript
|
|
523
509
|
let client = new Client();
|
|
524
|
-
|
|
510
|
+
|
|
525
511
|
let data = fs.createReadStream('/path/to/local/file.txt');
|
|
526
512
|
let remote = '/path/to/remote/file.txt';
|
|
527
|
-
|
|
513
|
+
|
|
528
514
|
client.connect(config)
|
|
529
515
|
.then(() => {
|
|
530
516
|
return client.put(data, remote);
|
|
@@ -536,7 +522,7 @@ Upload data from local system to remote server. If the `src` argument is a strin
|
|
|
536
522
|
console.error(err.message);
|
|
537
523
|
});
|
|
538
524
|
```
|
|
539
|
-
|
|
525
|
+
|
|
540
526
|
- **Tip:** If the src argument is a path string, consider just using `fastPut()`.
|
|
541
527
|
|
|
542
528
|
### fastPut(localPath, remotePath, options) ==> string<a id="sec-5-2-9"></a>
|
|
@@ -558,7 +544,7 @@ Uploads the data in file at `localPath` to a new file on remote server at `remot
|
|
|
558
544
|
// a part of a file was transferred
|
|
559
545
|
}
|
|
560
546
|
```
|
|
561
|
-
|
|
547
|
+
|
|
562
548
|
- **Warning:** There have been reports that some SFTP servers will not honour requests for non-default chunk sizes. This can result in data loss or corruption.
|
|
563
549
|
|
|
564
550
|
2. Example Use
|
|
@@ -567,7 +553,7 @@ Uploads the data in file at `localPath` to a new file on remote server at `remot
|
|
|
567
553
|
let localFile = '/path/to/file.txt';
|
|
568
554
|
let remoteFile = '/path/to/remote/file.txt';
|
|
569
555
|
let client = new Client();
|
|
570
|
-
|
|
556
|
+
|
|
571
557
|
client.connect(config)
|
|
572
558
|
.then(() => {
|
|
573
559
|
client.fastPut(localFile, remoteFile);
|
|
@@ -591,7 +577,7 @@ Append the `input` data to an existing remote file. There is no integrity checki
|
|
|
591
577
|
1. Options
|
|
592
578
|
|
|
593
579
|
The following options are supported;
|
|
594
|
-
|
|
580
|
+
|
|
595
581
|
```javascript
|
|
596
582
|
{
|
|
597
583
|
flags: 'a', // w - write and a - append
|
|
@@ -600,7 +586,7 @@ Append the `input` data to an existing remote file. There is no integrity checki
|
|
|
600
586
|
autoClose: true // automatically close the write stream when finished
|
|
601
587
|
}
|
|
602
588
|
```
|
|
603
|
-
|
|
589
|
+
|
|
604
590
|
The most common options to use are mode and encoding. The values shown above are the defaults. You do not have to set encoding to utf-8 for text files, null is fine for all file types. Generally, I would not attempt to append binary files.
|
|
605
591
|
|
|
606
592
|
2. Example Use
|
|
@@ -608,7 +594,7 @@ Append the `input` data to an existing remote file. There is no integrity checki
|
|
|
608
594
|
```javascript
|
|
609
595
|
let remotePath = '/path/to/remote/file.txt';
|
|
610
596
|
let client = new Client();
|
|
611
|
-
|
|
597
|
+
|
|
612
598
|
client.connect(config)
|
|
613
599
|
.then(() => {
|
|
614
600
|
return client.append(Buffer.from('Hello world'), remotePath);
|
|
@@ -633,7 +619,7 @@ Create a new directory. If the recursive flag is set to true, the method will cr
|
|
|
633
619
|
```javascript
|
|
634
620
|
let remoteDir = '/path/to/new/dir';
|
|
635
621
|
let client = new Client();
|
|
636
|
-
|
|
622
|
+
|
|
637
623
|
client.connect(config)
|
|
638
624
|
.then(() => {
|
|
639
625
|
return client.mkdir(remoteDir, true);
|
|
@@ -660,7 +646,7 @@ Remove a directory. If removing a directory and recursive flag is set to `true`,
|
|
|
660
646
|
```javascript
|
|
661
647
|
let remoteDir = '/path/to/remote/dir';
|
|
662
648
|
let client = new Client();
|
|
663
|
-
|
|
649
|
+
|
|
664
650
|
client.connect(config)
|
|
665
651
|
.then(() => {
|
|
666
652
|
return client.rmdir(remoteDir, true);
|
|
@@ -686,7 +672,7 @@ Delete a file on the remote server.
|
|
|
686
672
|
```javascript
|
|
687
673
|
let remoteFile = '/path/to/remote/file.txt';
|
|
688
674
|
let client = new Client();
|
|
689
|
-
|
|
675
|
+
|
|
690
676
|
client.connect(config)
|
|
691
677
|
.then(() => {
|
|
692
678
|
return client.delete(remoteFile);
|
|
@@ -712,7 +698,7 @@ Rename a file or directory from `fromPath` to `toPath`. You must have the necess
|
|
|
712
698
|
let from = '/remote/path/to/old.txt';
|
|
713
699
|
let to = '/remote/path/to/new.txt';
|
|
714
700
|
let client = new Client();
|
|
715
|
-
|
|
701
|
+
|
|
716
702
|
client.connect(config)
|
|
717
703
|
.then(() => {
|
|
718
704
|
return client.rename(from, to);
|
|
@@ -762,7 +748,7 @@ Change the mode (read, write or execute permissions) of a remote file or directo
|
|
|
762
748
|
let path = '/path/to/remote/file.txt';
|
|
763
749
|
let newMode = 0o644; // rw-r-r
|
|
764
750
|
let client = new Client();
|
|
765
|
-
|
|
751
|
+
|
|
766
752
|
client.connect(config)
|
|
767
753
|
.then(() => {
|
|
768
754
|
return client.chmod(path, newMode);
|
|
@@ -803,28 +789,28 @@ The optionsl *filter* argument is a regular expression which can be used to sele
|
|
|
803
789
|
|
|
804
790
|
```javascript
|
|
805
791
|
'use strict';
|
|
806
|
-
|
|
792
|
+
|
|
807
793
|
// Example of using the uploadDir() method to upload a directory
|
|
808
794
|
// to a remote SFTP server
|
|
809
|
-
|
|
795
|
+
|
|
810
796
|
const path = require('path');
|
|
811
797
|
const SftpClient = require('../src/index');
|
|
812
|
-
|
|
798
|
+
|
|
813
799
|
const dotenvPath = path.join(__dirname, '..', '.env');
|
|
814
800
|
require('dotenv').config({path: dotenvPath});
|
|
815
|
-
|
|
801
|
+
|
|
816
802
|
const config = {
|
|
817
803
|
host: process.env.SFTP_SERVER,
|
|
818
804
|
username: process.env.SFTP_USER,
|
|
819
805
|
password: process.env.SFTP_PASSWORD,
|
|
820
806
|
port: process.env.SFTP_PORT || 22
|
|
821
807
|
};
|
|
822
|
-
|
|
808
|
+
|
|
823
809
|
async function main() {
|
|
824
810
|
const client = new SftpClient('upload-test');
|
|
825
811
|
const src = path.join(__dirname, '..', 'test', 'testData', 'upload-src');
|
|
826
812
|
const dst = '/home/tim/upload-test';
|
|
827
|
-
|
|
813
|
+
|
|
828
814
|
try {
|
|
829
815
|
await client.connect(config);
|
|
830
816
|
client.on('upload', info => {
|
|
@@ -836,7 +822,7 @@ The optionsl *filter* argument is a regular expression which can be used to sele
|
|
|
836
822
|
client.end();
|
|
837
823
|
}
|
|
838
824
|
}
|
|
839
|
-
|
|
825
|
+
|
|
840
826
|
main()
|
|
841
827
|
.then(msg => {
|
|
842
828
|
console.log(msg);
|
|
@@ -844,7 +830,7 @@ The optionsl *filter* argument is a regular expression which can be used to sele
|
|
|
844
830
|
.catch(err => {
|
|
845
831
|
console.log(`main error: ${err.message}`);
|
|
846
832
|
});
|
|
847
|
-
|
|
833
|
+
|
|
848
834
|
```
|
|
849
835
|
|
|
850
836
|
### downloadDir(srcDir, dstDir, filter) ==> string<a id="sec-5-2-20"></a>
|
|
@@ -863,28 +849,28 @@ The optional *filter* argument is a regular expression which can be used to sele
|
|
|
863
849
|
|
|
864
850
|
```javascript
|
|
865
851
|
'use strict';
|
|
866
|
-
|
|
852
|
+
|
|
867
853
|
// Example of using the downloadDir() method to upload a directory
|
|
868
854
|
// to a remote SFTP server
|
|
869
|
-
|
|
855
|
+
|
|
870
856
|
const path = require('path');
|
|
871
857
|
const SftpClient = require('../src/index');
|
|
872
|
-
|
|
858
|
+
|
|
873
859
|
const dotenvPath = path.join(__dirname, '..', '.env');
|
|
874
860
|
require('dotenv').config({path: dotenvPath});
|
|
875
|
-
|
|
861
|
+
|
|
876
862
|
const config = {
|
|
877
863
|
host: process.env.SFTP_SERVER,
|
|
878
864
|
username: process.env.SFTP_USER,
|
|
879
865
|
password: process.env.SFTP_PASSWORD,
|
|
880
866
|
port: process.env.SFTP_PORT || 22
|
|
881
867
|
};
|
|
882
|
-
|
|
868
|
+
|
|
883
869
|
async function main() {
|
|
884
870
|
const client = new SftpClient('upload-test');
|
|
885
871
|
const dst = '/tmp';
|
|
886
872
|
const src = '/home/tim/upload-test';
|
|
887
|
-
|
|
873
|
+
|
|
888
874
|
try {
|
|
889
875
|
await client.connect(config);
|
|
890
876
|
client.on('download', info => {
|
|
@@ -896,7 +882,7 @@ The optional *filter* argument is a regular expression which can be used to sele
|
|
|
896
882
|
client.end();
|
|
897
883
|
}
|
|
898
884
|
}
|
|
899
|
-
|
|
885
|
+
|
|
900
886
|
main()
|
|
901
887
|
.then(msg => {
|
|
902
888
|
console.log(msg);
|
|
@@ -904,7 +890,7 @@ The optional *filter* argument is a regular expression which can be used to sele
|
|
|
904
890
|
.catch(err => {
|
|
905
891
|
console.log(`main error: ${err.message}`);
|
|
906
892
|
});
|
|
907
|
-
|
|
893
|
+
|
|
908
894
|
```
|
|
909
895
|
|
|
910
896
|
### end() ==> boolean<a id="sec-5-2-21"></a>
|
|
@@ -915,7 +901,7 @@ Ends the current client session, releasing the client socket and associated reso
|
|
|
915
901
|
|
|
916
902
|
```javascript
|
|
917
903
|
let client = new Client();
|
|
918
|
-
|
|
904
|
+
|
|
919
905
|
client.connect(config)
|
|
920
906
|
.then(() => {
|
|
921
907
|
// do some sftp stuff
|
|
@@ -934,11 +920,13 @@ Although normally not required, you can add and remove custom listeners on the s
|
|
|
934
920
|
|
|
935
921
|
- **error:** An error occurred. Calls listener with an error argument.
|
|
936
922
|
- **end:** The socket has been disconnected. No argument.
|
|
937
|
-
- **close:** The socket was closed.
|
|
923
|
+
- **close:** The socket was closed.
|
|
938
924
|
|
|
939
925
|
1. on(eventType, listener)
|
|
940
926
|
|
|
941
|
-
Adds the specified listener to the specified event type. It the event type is `error`, the listener should accept 1 argument, which will be an Error object.
|
|
927
|
+
Adds the specified listener to the specified event type. It the event type is `error`, the listener should accept 1 argument, which will be an Error object. The event handlers for `end` and `close` events have no arguments.
|
|
928
|
+
|
|
929
|
+
The handlers will be added to the beginning of the listener's event handlers, so it will be called before any of the `ssh2-sftp-client` listeners.
|
|
942
930
|
|
|
943
931
|
2. removeListener(eventType, listener)
|
|
944
932
|
|
|
@@ -948,23 +936,29 @@ Although normally not required, you can add and remove custom listeners on the s
|
|
|
948
936
|
|
|
949
937
|
## Server Capabilities<a id="sec-6-1"></a>
|
|
950
938
|
|
|
951
|
-
All SFTP servers and platforms are not equal. Some facilities provided by `ssh2-
|
|
939
|
+
All SFTP servers and platforms are not equal. Some facilities provided by `ssh2-sftp-client` either depend on capabilities of the remote server or the underlying capabilities of the remote server platform. As an example, consider `chmod()`. This command depends on a remote filesystem which implements the 'nix' concept of users and groups. The *win32* platform does not have the same concept of users and groups, so `chmod()` will not behave in the same way.
|
|
952
940
|
|
|
953
941
|
One way to determine whether an issue you are encountering is due to `ssh2-sftp-client` or due to the remote server or server platform is to use a simple CLI sftp program, such as openSSH's sftp command. If you observe the same behaviour using plain `sftp` on the command line, the issue is likely due to server or remote platform limitations. Note that you should not use a GUI sftp client, like `Filezilla` or `winSCP` as such GUI programs often attempt to hide these server and platform incompatibilities and will take additional steps to simulate missing functionality etc. You want to use a CLI program which does as little as possible.
|
|
954
942
|
|
|
955
|
-
|
|
943
|
+
## Promises, Events & Managing Exceptions<a id="sec-6-2"></a>
|
|
944
|
+
|
|
945
|
+
One of the challenges in providing a Promise based API over a module like SSH2, which is event based is how to ensure events are handled appropriately. The challenge is due to the synchronous nature of events. You cannot use `try/catch` for events because you have no way of knowing when the event might fire. For example, it could easily fire after your `try/catch` block as completed execution.
|
|
946
|
+
|
|
947
|
+
Things become even more complicated once you mix in Promises. When you define a promise, you have to methods which can be called to fulfil a promise, `resolve` and `reject`. Only one can be called - once you call `resolve`, you cannot call `reject` (well, you can call it, but it won't have any impact on the fulfilment status of the promise). The problem arises when an event, for exmaple an `error` event is fired either after you have resolved a promise or possibly in-between promises. If you don't catch the `error` event, your script will likely crash with an `uncaught exception` error.
|
|
956
948
|
|
|
957
|
-
|
|
949
|
+
To make matters worse, some servers, particularly servers running on a Windows platform, will raise multiple errors for the same error *event*. For example, when you attempt to connect with a bad username or password, you will get a `All authentication methods have failed` exception. However, under Windows, you will also get a `Connection reset by peer` exception. If we reject the connect promise based on the authentication failure exception, what do we do with the `reset by peer` exception? More critically, what will handle that exception given the promise has already been fulfilled and completed? To make matters worse, it seems that Windows based servers also raise an error event for *non-errors*. For example, when you call the `end()` method, the connection is closed. On windows, this also results in a *connection reset by peer* error. While it could be argued that the remote server resetting the connection after receiving a disconnect request is not an error, it doesn't change the fact that one is raised and we need to somehow deal with it.
|
|
958
950
|
|
|
959
|
-
|
|
951
|
+
To handle this, `ssh2-sftp-client` implements a couple of strategies. Firstly, when you call one of the module's methods, it adds `error`, `end` and `close` event listeners which will call the `reject` moethod on the enclosing promise. It also keeps track of whether an error has been handled and if it has, it ignores any subsequent errors until the promise ends. Typically, the first error caught has the most relevant information and any subsequent error events are less critical or informative, so ignoring them has no negative impact. Provided one of the events is raised before the promise is fulfilled, these handlers will consume the event and deal with it appropriately.
|
|
960
952
|
|
|
961
|
-
|
|
953
|
+
In testing, it was found that in some situations, particularly during connect operations, subsequent errors fired with a small delay. This prevents the errors from being handled by the event handlers associated with the connect promise. To deal with this, a small 500ms delay has been added to the connect() method, which effectively delays the removal of the event handlers until all events have been caught.
|
|
962
954
|
|
|
963
|
-
|
|
955
|
+
The other area where additional events are fired is during the end() call. To deal with these events, the `end()` method setus up listeners which will simply ignore additional `error`, `end` and `close` events. It is assumed that once you have called `end()` you really only care about any main error which occurs and no longer care about other errors that may be raised as the connection is terminated.
|
|
964
956
|
|
|
965
|
-
|
|
957
|
+
In addition to the promise based event handlers, `ssh2-sftp-client` also implements global event handlers which will catch any `error`, `end` or `close` events. Essentially, these global handlers only reset the `sftp` property of the client object, effectively ensuring any subsequent calls are rejected and in the case of an error, send the error to the console.
|
|
966
958
|
|
|
967
|
-
|
|
959
|
+
### Adding Custom Handlers<a id="sec-6-2-1"></a>
|
|
960
|
+
|
|
961
|
+
While the above strategies appear to work for the majority of use cases, there are always going to be edge cases which require more flexible or powerful event handling. To support this, the `on()` and `removeListener()` methods are provided. Any event listener added using the `on()` method will be added at the beginning of the list of handlers for that event, ensuring it will be called before any global or promise local events. See the documentation for the `on()` method for details.
|
|
968
962
|
|
|
969
963
|
## Windows Based Servers<a id="sec-6-3"></a>
|
|
970
964
|
|
|
@@ -1113,6 +1107,8 @@ client.connect({
|
|
|
1113
1107
|
|
|
1114
1108
|
Some users have encountered the error 'Timeout while waiting for handshake' or 'Handshake failed, no matching client->server ciphers. This is often due to the client not having the correct configuration for the transport layer algorithms used by ssh2. One of the connect options provided by the ssh2 module is `algorithm`, which is an object that allows you to explicitly set the key exchange, ciphers, hmac and compression algorithms as well as server host key used to establish the initial secure connection. See the SSH2 documentation for details. Getting these parameters correct usually resolves the issue.
|
|
1115
1109
|
|
|
1110
|
+
When encountering this type of problem, one worthwhile approach is to use openSSH's CLI sftp program with the `-v` switch to raise loggin levels. This will show you what algorithms the CLI is using. You can then use this information to match the names with the accepted algorithm names documented in the `ssh2` README to set the properties in the `algorithms` object.
|
|
1111
|
+
|
|
1116
1112
|
## How can I limit upload/download speed<a id="sec-7-6"></a>
|
|
1117
1113
|
|
|
1118
1114
|
If you want to limit the amount of bandwidth used during upload/download of data, you can use a stream to limit throughput. The following example was provided by *kennylbj*. Note that there is a caveat that we must set the `autoClose` flag to false to avoid calling an extra `_read()` on a closed stream that may cause \_get Permission Denied error in ssh2-streams.
|
|
@@ -1150,6 +1146,14 @@ try {
|
|
|
1150
1146
|
}
|
|
1151
1147
|
```
|
|
1152
1148
|
|
|
1149
|
+
## Connection hangs or fails for larger files<a id="sec-7-7"></a>
|
|
1150
|
+
|
|
1151
|
+
This was contributed by Ladislav Jacho. Thanks.
|
|
1152
|
+
|
|
1153
|
+
A symptom of this issue is that you are able to upload small files, but uploading larger ones fail. You probably have an MTU/fragmentation problem. For each network interface on both client and server set the MTU to 576, e.g. `ifconfig eth0 mtu 576`. If that works, you need to find the largest MTU which will work for your network. An MTU which is too small will adversely affect throughput speed. A common value to use is an MTU of 1400.
|
|
1154
|
+
|
|
1155
|
+
For more explanation, see [issue #342](https://github.com/theophilusx/ssh2-sftp-client/issues/342).
|
|
1156
|
+
|
|
1153
1157
|
# Examples<a id="sec-8"></a>
|
|
1154
1158
|
|
|
1155
1159
|
I have started collecting example scripts in the example directory of the repository. These are mainly scripts I have put together in order to investigate issues or provide samples for users. They are not robust, lack adequate error handling and may contain errors. However, I think they are still useful for helping developers see how the module and API can be used.
|
|
@@ -1327,6 +1331,8 @@ Perhaps the best assistance is a minimal reproducible example of the issue. Once
|
|
|
1327
1331
|
|
|
1328
1332
|
Pull requests are always welcomed. However, please ensure your changes pass all tests and if your adding a new feature, that tests for that feature are included. Likewise, for new features or enhancements, please include any relevant documentation updates.
|
|
1329
1333
|
|
|
1334
|
+
**Note**: The `README.md` file is generated from the `README.org` file. Therefore, any documentation updates or fixes need to be made to the `README.org` file. This file is *tangled* using `Emacs` org mode. If you don't use Emacs or org-mode, don't be too concerned. The org-mode syntax is straight-forward and similar to *markdown*. I will verify any updates to `README.org` and generate a new `README.md` when necessary. The main point to note is that any changes made directly to `README.md` will not persist and will be lost when a new version is generated, so don't modify that file.
|
|
1335
|
+
|
|
1330
1336
|
This module will adopt a standard semantic versioning policy. Please indicate in your pull request what level of change it represents i.e.
|
|
1331
1337
|
|
|
1332
1338
|
- **Major:** Change to API or major change in functionality which will require an increase in major version number.
|
|
@@ -1348,3 +1354,5 @@ Thanks to the following for their contributions -
|
|
|
1348
1354
|
- **teenangst:** Contributed fix for error code 4 in stat() method
|
|
1349
1355
|
- **kennylbj:** Contributed example of using a throttle stream to limit upload/download bandwidth.
|
|
1350
1356
|
- **anton-erofeev:** Documentation fix
|
|
1357
|
+
- **Ladislav Jacho:** Contributed solution explanation for connections hanging when transferring larger files.
|
|
1358
|
+
- **Emma Milner:** Contributed fix for put() bug
|