molex-ftp-client 1.2.1 → 2.1.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/CHANGELOG.md +47 -0
- package/README.md +109 -331
- package/benchmark.js +86 -0
- package/lib/FTPClient.js +103 -46
- package/lib/commands.js +132 -44
- package/lib/connection.js +11 -8
- package/lib/performance.js +29 -0
- package/lib/utils.js +0 -19
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.1.0] - 2026-02-02
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `stat()` method returns detailed file/directory information: `{ exists, size, isFile, isDirectory }`
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- **Simplified performance system** - removed over-engineered preset configuration
|
|
10
|
+
- TCP optimizations (TCP_NODELAY, keep-alive) now applied by default at socket creation
|
|
11
|
+
- `createOptimizedSocket()` replaces repeated `optimizeSocket()` calls for cleaner code
|
|
12
|
+
- Updated `exists()` to use new `stat()` method internally
|
|
13
|
+
|
|
14
|
+
### Removed
|
|
15
|
+
- Performance presets (LOW_LATENCY, HIGH_THROUGHPUT, BALANCED) - unnecessary complexity
|
|
16
|
+
- `performancePreset` and `performance` constructor options
|
|
17
|
+
- `getOptimalChunkSize()` function - Node.js doesn't expose socket buffer controls
|
|
18
|
+
|
|
19
|
+
## [2.0.0] - 2026-02-02
|
|
20
|
+
|
|
21
|
+
### Breaking Changes
|
|
22
|
+
- Removed `ensureParentDir()` - use `ensureDir(path, true, true)` instead
|
|
23
|
+
- Removed `uploadFile()` - use `upload(data, path, true)` instead
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
- **Performance optimization system** with TCP tuning (inspired by HFT practices)
|
|
27
|
+
- `downloadStream()` method for memory-efficient large file transfers
|
|
28
|
+
- `isConnected()` method to check connection and authentication status
|
|
29
|
+
- Parameter validation for `connect()`, `upload()`, and `download()`
|
|
30
|
+
- Connection state validation before upload/download operations
|
|
31
|
+
- Better error messages with file path context
|
|
32
|
+
- Data socket cleanup in connection close
|
|
33
|
+
- New `lib/performance.js` module for TCP optimization utilities
|
|
34
|
+
|
|
35
|
+
### Improved
|
|
36
|
+
- **40-50% faster downloads** for small files with LOW_LATENCY preset
|
|
37
|
+
- **`exists()` now properly detects directories** (previously only detected files)
|
|
38
|
+
- All timeouts now respect `client.timeout` configuration (no more hardcoded values)
|
|
39
|
+
- Error messages include file paths for better debugging
|
|
40
|
+
- Connection cleanup includes data socket termination
|
|
41
|
+
- Code quality: added missing semicolons and consistent formatting
|
|
42
|
+
- All data connections now apply performance optimizations
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
- `upload()` now accepts optional `ensureDir` boolean parameter (default: false)
|
|
46
|
+
- `ensureDir()` now accepts optional `isFilePath` boolean parameter (default: false)
|
|
47
|
+
- Consolidated API reduces method count while maintaining functionality
|
|
48
|
+
- TCP sockets now optimized on connection for better performance
|
|
49
|
+
|
|
3
50
|
## [1.2.1] - 2026-02-02
|
|
4
51
|
|
|
5
52
|
### Changed
|
package/README.md
CHANGED
|
@@ -1,22 +1,15 @@
|
|
|
1
1
|
# molex-ftp-client
|
|
2
2
|
|
|
3
|
-
Lightweight FTP client built with native Node.js TCP sockets
|
|
3
|
+
Lightweight FTP client built with native Node.js TCP sockets. Zero dependencies, optimized for performance.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- ✅ **Event-based** - Listen to FTP responses and events
|
|
14
|
-
- ✅ **Upload/download** files with Buffer support
|
|
15
|
-
- ✅ **Smart directory management** - Auto-create nested directories
|
|
16
|
-
- ✅ **Directory operations** (list, cd, mkdir, pwd, ensureDir)
|
|
17
|
-
- ✅ **File operations** (delete, rename, size, exists, modifiedTime)
|
|
18
|
-
- ✅ **Connection statistics** - Track command count and status
|
|
19
|
-
- ✅ **Clean architecture** - Modular structure with separation of concerns
|
|
7
|
+
- **Zero dependencies** - Uses only native Node.js modules
|
|
8
|
+
- **Promise-based API** - Modern async/await support
|
|
9
|
+
- **TCP optimizations** - TCP_NODELAY and keep-alive applied by default
|
|
10
|
+
- **Auto-create directories** - Upload files to nested paths automatically
|
|
11
|
+
- **Streaming support** - Memory-efficient downloads for large files
|
|
12
|
+
- **Full FTP support** - Upload, download, list, delete, rename, stat, and more
|
|
20
13
|
|
|
21
14
|
## Installation
|
|
22
15
|
|
|
@@ -29,344 +22,166 @@ npm install molex-ftp-client
|
|
|
29
22
|
```javascript
|
|
30
23
|
const FTPClient = require('molex-ftp-client');
|
|
31
24
|
|
|
32
|
-
const client = new FTPClient(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
const client = new FTPClient();
|
|
26
|
+
|
|
27
|
+
await client.connect({
|
|
28
|
+
host: 'ftp.example.com',
|
|
29
|
+
user: 'username',
|
|
30
|
+
password: 'password'
|
|
36
31
|
});
|
|
37
32
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
// Upload file
|
|
48
|
-
await client.upload('Hello World!', '/remote/path/file.txt');
|
|
49
|
-
|
|
50
|
-
// Download file
|
|
51
|
-
const data = await client.download('/remote/path/file.txt');
|
|
52
|
-
console.log(data.toString());
|
|
53
|
-
|
|
54
|
-
// Close connection
|
|
55
|
-
await client.close();
|
|
56
|
-
} catch (err) {
|
|
57
|
-
console.error('FTP Error:', err);
|
|
58
|
-
}
|
|
33
|
+
// Upload with auto-directory creation
|
|
34
|
+
await client.upload('Hello World!', '/path/to/file.txt', true);
|
|
35
|
+
|
|
36
|
+
// Download
|
|
37
|
+
const data = await client.download('/path/to/file.txt');
|
|
38
|
+
console.log(data.toString());
|
|
39
|
+
|
|
40
|
+
await client.close();
|
|
59
41
|
```
|
|
60
42
|
|
|
61
43
|
## Constructor Options
|
|
62
44
|
|
|
63
45
|
```javascript
|
|
64
46
|
const client = new FTPClient({
|
|
65
|
-
debug: false,
|
|
66
|
-
timeout: 30000,
|
|
67
|
-
|
|
68
|
-
logger: console.log // Custom logger function
|
|
47
|
+
debug: false, // Enable debug logging
|
|
48
|
+
timeout: 30000, // Command timeout in milliseconds (default: 30000)
|
|
49
|
+
logger: console.log // Custom logger function
|
|
69
50
|
});
|
|
70
51
|
```
|
|
71
52
|
|
|
72
|
-
## API
|
|
53
|
+
## API Reference
|
|
73
54
|
|
|
74
|
-
### Connection
|
|
55
|
+
### Connection Methods
|
|
75
56
|
|
|
76
57
|
#### `connect(options)`
|
|
77
|
-
|
|
78
|
-
Connect to FTP server.
|
|
79
|
-
|
|
80
58
|
```javascript
|
|
81
59
|
await client.connect({
|
|
82
|
-
host: 'ftp.example.com',
|
|
83
|
-
port: 21,
|
|
84
|
-
user: 'username',
|
|
85
|
-
password: 'password'
|
|
60
|
+
host: 'ftp.example.com', // Required
|
|
61
|
+
port: 21, // Default: 21
|
|
62
|
+
user: 'username', // Default: 'anonymous'
|
|
63
|
+
password: 'password' // Default: 'anonymous@'
|
|
86
64
|
});
|
|
87
65
|
```
|
|
88
66
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
#### `close()` / `disconnect()`
|
|
92
|
-
|
|
93
|
-
Close connection to FTP server.
|
|
94
|
-
|
|
67
|
+
#### `close()`
|
|
95
68
|
```javascript
|
|
96
69
|
await client.close();
|
|
97
70
|
```
|
|
98
71
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
### File Operations
|
|
102
|
-
|
|
103
|
-
#### `upload(data, remotePath)`
|
|
104
|
-
|
|
105
|
-
Upload file to server.
|
|
72
|
+
### File Methods
|
|
106
73
|
|
|
74
|
+
#### `upload(data, remotePath, ensureDir)`
|
|
107
75
|
```javascript
|
|
108
|
-
//
|
|
109
|
-
await client.upload(
|
|
110
|
-
|
|
111
|
-
// Upload Buffer
|
|
112
|
-
const buffer = Buffer.from('data');
|
|
113
|
-
await client.upload(buffer, '/path/file.bin');
|
|
76
|
+
await client.upload('content', '/path/file.txt'); // Basic upload
|
|
77
|
+
await client.upload(buffer, '/path/file.bin'); // Upload Buffer
|
|
78
|
+
await client.upload('content', '/deep/path/file.txt', true); // Auto-create dirs
|
|
114
79
|
```
|
|
115
80
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
#### `uploadFile(data, remotePath, ensureDir)`
|
|
119
|
-
|
|
120
|
-
Upload file and optionally ensure parent directory exists. This is the recommended method when uploading to deep paths.
|
|
121
|
-
|
|
81
|
+
#### `download(remotePath)` → `Buffer`
|
|
122
82
|
```javascript
|
|
123
|
-
|
|
124
|
-
await client.uploadFile('data', '/deep/nested/path/file.txt', true);
|
|
125
|
-
|
|
126
|
-
// Upload without directory creation
|
|
127
|
-
await client.uploadFile('data', '/file.txt', false);
|
|
83
|
+
const data = await client.download('/path/file.txt');
|
|
128
84
|
```
|
|
129
85
|
|
|
130
|
-
|
|
131
|
-
- `data` (string|Buffer): File content
|
|
132
|
-
- `remotePath` (string): Remote file path
|
|
133
|
-
- `ensureDir` (boolean): Create parent directories if needed (default: false)
|
|
134
|
-
|
|
135
|
-
**Why use this?** Simplifies uploads to nested paths by automatically creating any missing parent directories.
|
|
136
|
-
|
|
137
|
-
Returns: `Promise<void>`
|
|
138
|
-
|
|
139
|
-
#### `download(remotePath)`
|
|
140
|
-
|
|
141
|
-
Download file from server.
|
|
142
|
-
|
|
86
|
+
#### `downloadStream(remotePath, writeStream)` → `number` (bytes)
|
|
143
87
|
```javascript
|
|
144
|
-
const
|
|
145
|
-
|
|
88
|
+
const fs = require('fs');
|
|
89
|
+
const stream = fs.createWriteStream('./local.bin');
|
|
90
|
+
const bytes = await client.downloadStream('/remote.bin', stream);
|
|
146
91
|
```
|
|
147
92
|
|
|
148
|
-
Returns: `Promise<Buffer>`
|
|
149
|
-
|
|
150
93
|
#### `delete(path)`
|
|
151
|
-
|
|
152
|
-
Delete file.
|
|
153
|
-
|
|
154
94
|
```javascript
|
|
155
95
|
await client.delete('/path/file.txt');
|
|
156
96
|
```
|
|
157
97
|
|
|
158
|
-
Returns: `Promise<void>`
|
|
159
|
-
|
|
160
98
|
#### `rename(from, to)`
|
|
161
|
-
|
|
162
|
-
Rename or move file.
|
|
163
|
-
|
|
164
|
-
```javascript
|
|
165
|
-
await client.rename('/old/path.txt', '/new/path.txt');
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
Returns: `Promise<void>`
|
|
169
|
-
|
|
170
|
-
#### `size(path)`
|
|
171
|
-
|
|
172
|
-
Get file size in bytes.
|
|
173
|
-
|
|
174
99
|
```javascript
|
|
175
|
-
|
|
176
|
-
console.log(`File size: ${bytes} bytes`);
|
|
100
|
+
await client.rename('/old.txt', '/new.txt');
|
|
177
101
|
```
|
|
178
102
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
#### `exists(path)`
|
|
182
|
-
|
|
183
|
-
Check if file exists.
|
|
184
|
-
|
|
103
|
+
#### `exists(path)` → `boolean`
|
|
185
104
|
```javascript
|
|
186
105
|
const exists = await client.exists('/path/file.txt');
|
|
187
|
-
console.log(exists ? 'File exists' : 'File not found');
|
|
188
106
|
```
|
|
189
107
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
#### `modifiedTime(path)`
|
|
193
|
-
|
|
194
|
-
Get file modification time.
|
|
195
|
-
|
|
108
|
+
#### `stat(path)` → `Object`
|
|
109
|
+
Get detailed file/directory information.
|
|
196
110
|
```javascript
|
|
197
|
-
const
|
|
198
|
-
|
|
111
|
+
const info = await client.stat('/path/file.txt');
|
|
112
|
+
// { exists: true, size: 1024, isFile: true, isDirectory: false }
|
|
199
113
|
```
|
|
200
114
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
### Directory Operations
|
|
204
|
-
|
|
205
|
-
#### `list(path)`
|
|
206
|
-
|
|
207
|
-
List directory contents.
|
|
208
|
-
|
|
115
|
+
#### `size(path)` → `number`
|
|
209
116
|
```javascript
|
|
210
|
-
const
|
|
211
|
-
console.log(listing);
|
|
117
|
+
const bytes = await client.size('/path/file.txt');
|
|
212
118
|
```
|
|
213
119
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
#### `cd(path)`
|
|
217
|
-
|
|
218
|
-
Change working directory.
|
|
120
|
+
### Directory Methods
|
|
219
121
|
|
|
122
|
+
#### `list(path)` → `string`
|
|
220
123
|
```javascript
|
|
221
|
-
await client.
|
|
124
|
+
const listing = await client.list('/path');
|
|
222
125
|
```
|
|
223
126
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
#### `pwd()`
|
|
227
|
-
|
|
228
|
-
Get current working directory.
|
|
229
|
-
|
|
127
|
+
#### `mkdir(path)`
|
|
230
128
|
```javascript
|
|
231
|
-
|
|
232
|
-
console.log(`Current directory: ${dir}`);
|
|
129
|
+
await client.mkdir('/path/newdir');
|
|
233
130
|
```
|
|
234
131
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
#### `mkdir(path)`
|
|
238
|
-
|
|
239
|
-
Create directory.
|
|
240
|
-
|
|
132
|
+
#### `cd(path)`
|
|
241
133
|
```javascript
|
|
242
|
-
await client.
|
|
134
|
+
await client.cd('/path/to/directory');
|
|
243
135
|
```
|
|
244
136
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
#### `ensureDir(dirPath, recursive)`
|
|
248
|
-
|
|
249
|
-
Ensure directory exists, creating it (and parent directories) if necessary. Idempotent - safe to call multiple times.
|
|
250
|
-
|
|
137
|
+
#### `pwd()` → `string`
|
|
251
138
|
```javascript
|
|
252
|
-
|
|
253
|
-
await client.ensureDir('/deep/nested/path');
|
|
254
|
-
|
|
255
|
-
// Create single directory only (will fail if parent doesn't exist)
|
|
256
|
-
await client.ensureDir('/newdir', false);
|
|
257
|
-
|
|
258
|
-
// Idempotent - no error if directory already exists
|
|
259
|
-
await client.ensureDir('/existing/path'); // No error
|
|
139
|
+
const currentDir = await client.pwd();
|
|
260
140
|
```
|
|
261
141
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
- `recursive` (boolean): Create parent directories if needed (default: true)
|
|
265
|
-
|
|
266
|
-
**Use case:** Preparing directory structure before multiple file uploads.
|
|
267
|
-
|
|
268
|
-
Returns: `Promise<void>`
|
|
269
|
-
|
|
270
|
-
#### `ensureParentDir(filePath)`
|
|
271
|
-
|
|
272
|
-
Ensure the parent directory exists for a given file path. Recursively creates all missing parent directories.
|
|
273
|
-
|
|
142
|
+
#### `ensureDir(dirPath, recursive, isFilePath)`
|
|
143
|
+
Create directory if it doesn't exist, optionally creating parent directories.
|
|
274
144
|
```javascript
|
|
275
|
-
|
|
276
|
-
await client.
|
|
277
|
-
await client.upload('data', '/path/to/file.txt');
|
|
278
|
-
|
|
279
|
-
// Tip: Use uploadFile() instead for convenience
|
|
280
|
-
await client.uploadFile('data', '/path/to/file.txt', true); // Equivalent
|
|
145
|
+
await client.ensureDir('/deep/nested/path'); // Create full path
|
|
146
|
+
await client.ensureDir('/path/file.txt', true, true); // Ensure parent dir for file
|
|
281
147
|
```
|
|
282
148
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
Returns: `Promise<void>`
|
|
286
|
-
|
|
287
|
-
### Utilities
|
|
288
|
-
|
|
289
|
-
#### `getStats()`
|
|
290
|
-
|
|
291
|
-
Get connection statistics.
|
|
149
|
+
### Utility Methods
|
|
292
150
|
|
|
151
|
+
#### `getState()` → `Object`
|
|
152
|
+
Get current client state for debugging.
|
|
293
153
|
```javascript
|
|
294
|
-
const
|
|
295
|
-
console.log(stats);
|
|
154
|
+
const state = client.getState();
|
|
296
155
|
// {
|
|
297
156
|
// connected: true,
|
|
298
157
|
// authenticated: true,
|
|
299
|
-
//
|
|
300
|
-
//
|
|
158
|
+
// host: 'ftp.example.com',
|
|
159
|
+
// ...
|
|
301
160
|
// }
|
|
302
161
|
```
|
|
303
162
|
|
|
304
|
-
Returns: `Object`
|
|
305
|
-
|
|
306
163
|
#### `setDebug(enabled)`
|
|
307
|
-
|
|
308
|
-
Enable or disable debug mode at runtime.
|
|
309
|
-
|
|
164
|
+
Toggle debug mode at runtime.
|
|
310
165
|
```javascript
|
|
311
|
-
client.setDebug(true);
|
|
312
|
-
client.setDebug(false); // Disable debug logging
|
|
166
|
+
client.setDebug(true);
|
|
313
167
|
```
|
|
314
168
|
|
|
315
169
|
## Events
|
|
316
170
|
|
|
317
|
-
The client extends EventEmitter and emits the following events:
|
|
318
|
-
|
|
319
|
-
### `connected`
|
|
320
|
-
|
|
321
|
-
Fired when TCP connection is established.
|
|
322
|
-
|
|
323
|
-
```javascript
|
|
324
|
-
client.on('connected', () => {
|
|
325
|
-
console.log('Connected to FTP server');
|
|
326
|
-
});
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
### `response`
|
|
330
|
-
|
|
331
|
-
Fired for each FTP response (useful for debugging).
|
|
332
|
-
|
|
333
|
-
```javascript
|
|
334
|
-
client.on('response', (line) => {
|
|
335
|
-
console.log('FTP:', line);
|
|
336
|
-
});
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
### `error`
|
|
340
|
-
|
|
341
|
-
Fired on connection errors.
|
|
342
|
-
|
|
343
|
-
```javascript
|
|
344
|
-
client.on('error', (err) => {
|
|
345
|
-
console.error('FTP Error:', err);
|
|
346
|
-
});
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
### `close`
|
|
350
|
-
|
|
351
|
-
Fired when connection is closed.
|
|
352
|
-
|
|
353
171
|
```javascript
|
|
354
|
-
client.on('
|
|
355
|
-
|
|
356
|
-
|
|
172
|
+
client.on('connected', () => console.log('TCP connection established'));
|
|
173
|
+
client.on('response', (line) => console.log('FTP:', line));
|
|
174
|
+
client.on('error', (err) => console.error('Error:', err));
|
|
175
|
+
client.on('close', () => console.log('Connection closed'));
|
|
357
176
|
```
|
|
358
177
|
|
|
359
|
-
##
|
|
178
|
+
## Debugging
|
|
360
179
|
|
|
361
|
-
Enable debug
|
|
180
|
+
Enable debug mode to see all FTP commands and responses:
|
|
362
181
|
|
|
363
182
|
```javascript
|
|
364
183
|
const client = new FTPClient({ debug: true });
|
|
365
184
|
|
|
366
|
-
client.on('response', (line) => {
|
|
367
|
-
console.log('FTP Response:', line);
|
|
368
|
-
});
|
|
369
|
-
|
|
370
185
|
await client.connect({ host: 'ftp.example.com', user: 'user', password: 'pass' });
|
|
371
186
|
// [FTP Debug] Connecting to ftp.example.com:21 as user
|
|
372
187
|
// [FTP Debug] TCP connection established
|
|
@@ -375,12 +190,24 @@ await client.connect({ host: 'ftp.example.com', user: 'user', password: 'pass' }
|
|
|
375
190
|
// [FTP Debug] <<< 331 Password required
|
|
376
191
|
// [FTP Debug] >>> PASS ********
|
|
377
192
|
// [FTP Debug] <<< 230 Login successful
|
|
378
|
-
// [FTP Debug] Authentication successful
|
|
379
193
|
```
|
|
380
194
|
|
|
381
|
-
##
|
|
195
|
+
## Performance
|
|
382
196
|
|
|
383
|
-
|
|
197
|
+
TCP optimizations are automatically applied:
|
|
198
|
+
- **TCP_NODELAY** - Disables Nagle's algorithm for lower latency
|
|
199
|
+
- **Keep-alive** - Detects dead connections (10s interval)
|
|
200
|
+
|
|
201
|
+
For large files, use `downloadStream()` for memory-efficient transfers:
|
|
202
|
+
|
|
203
|
+
```javascript
|
|
204
|
+
const fs = require('fs');
|
|
205
|
+
const stream = fs.createWriteStream('./large.zip');
|
|
206
|
+
const bytes = await client.downloadStream('/backup.zip', stream);
|
|
207
|
+
console.log(`Downloaded ${bytes} bytes`);
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Error Handling
|
|
384
211
|
|
|
385
212
|
```javascript
|
|
386
213
|
try {
|
|
@@ -388,94 +215,45 @@ try {
|
|
|
388
215
|
} catch (err) {
|
|
389
216
|
if (err.message.includes('FTP Error 550')) {
|
|
390
217
|
console.error('Permission denied');
|
|
391
|
-
} else {
|
|
392
|
-
console.error('Upload failed:', err.message);
|
|
393
218
|
}
|
|
394
219
|
}
|
|
395
220
|
```
|
|
396
221
|
|
|
397
|
-
##
|
|
222
|
+
## Example
|
|
398
223
|
|
|
399
224
|
```javascript
|
|
400
225
|
const FTPClient = require('molex-ftp-client');
|
|
401
226
|
|
|
402
|
-
async function
|
|
403
|
-
const client = new FTPClient({
|
|
404
|
-
debug: true,
|
|
405
|
-
timeout: 60000
|
|
406
|
-
});
|
|
227
|
+
async function main() {
|
|
228
|
+
const client = new FTPClient({ debug: true });
|
|
407
229
|
|
|
408
230
|
try {
|
|
409
|
-
// Connect
|
|
410
231
|
await client.connect({
|
|
411
|
-
host: 'ftp.
|
|
412
|
-
port: 21,
|
|
232
|
+
host: 'ftp.example.com',
|
|
413
233
|
user: 'admin',
|
|
414
|
-
password: '
|
|
234
|
+
password: 'secret'
|
|
415
235
|
});
|
|
416
236
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
const oldData = await client.download('/backup/data.json');
|
|
424
|
-
console.log('Old backup size:', oldData.length, 'bytes');
|
|
425
|
-
|
|
426
|
-
// Get modification time
|
|
427
|
-
const modTime = await client.modifiedTime('/backup/data.json');
|
|
428
|
-
console.log('Last modified:', modTime.toISOString());
|
|
429
|
-
|
|
430
|
-
// Rename old backup
|
|
431
|
-
await client.rename('/backup/data.json', '/backup/data.old.json');
|
|
237
|
+
// Check file info
|
|
238
|
+
const info = await client.stat('/backup/data.json');
|
|
239
|
+
if (info.exists) {
|
|
240
|
+
console.log(`File size: ${info.size} bytes`);
|
|
241
|
+
const data = await client.download('/backup/data.json');
|
|
242
|
+
console.log('Downloaded:', data.toString());
|
|
432
243
|
}
|
|
433
244
|
|
|
434
|
-
// Upload new
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
console.log('Backup uploaded successfully');
|
|
438
|
-
|
|
439
|
-
// Verify
|
|
440
|
-
const size = await client.size('/backup/data.json');
|
|
441
|
-
console.log('New backup size:', size, 'bytes');
|
|
442
|
-
|
|
443
|
-
// Get stats
|
|
444
|
-
const stats = client.getStats();
|
|
445
|
-
console.log('Commands executed:', stats.commandCount);
|
|
446
|
-
|
|
447
|
-
// Close connection
|
|
245
|
+
// Upload new file
|
|
246
|
+
await client.upload('new data', '/backup/updated.json', true);
|
|
247
|
+
|
|
448
248
|
await client.close();
|
|
449
249
|
} catch (err) {
|
|
450
|
-
console.error('
|
|
451
|
-
await client.close();
|
|
250
|
+
console.error('FTP Error:', err.message);
|
|
452
251
|
}
|
|
453
252
|
}
|
|
454
253
|
|
|
455
|
-
|
|
254
|
+
main();
|
|
456
255
|
```
|
|
457
256
|
|
|
458
|
-
## Architecture
|
|
459
|
-
|
|
460
|
-
The library is organized with clean separation of concerns:
|
|
461
|
-
|
|
462
|
-
```
|
|
463
|
-
molex-ftp-client/
|
|
464
|
-
├── index.js # Entry point - exports FTPClient
|
|
465
|
-
├── lib/
|
|
466
|
-
│ ├── FTPClient.js # Main class definition & public API
|
|
467
|
-
│ ├── connection.js # Connection & authentication logic
|
|
468
|
-
│ ├── commands.js # FTP command implementations
|
|
469
|
-
│ └── utils.js # Helper functions (parsing, paths)
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
**Benefits:**
|
|
473
|
-
- 📁 **Modular structure** - Easy to maintain and extend
|
|
474
|
-
- 🔍 **Clear responsibilities** - Each file has a single purpose
|
|
475
|
-
- 🧪 **Testable** - Isolated components for unit testing
|
|
476
|
-
- 📖 **Readable** - Simple entry point with organized implementation
|
|
477
|
-
|
|
478
257
|
## License
|
|
479
258
|
|
|
480
|
-
ISC
|
|
481
|
-
|
|
259
|
+
ISC License
|