basic-ftp
Advanced tools
Comparing version 4.4.1 to 4.5.0
# Changelog | ||
## 4.5.0 | ||
- Added: Directory listings are included in transfer progress tracking. | ||
- Fixed: Possible edge case where socket is disconnected but client still says it's open. | ||
## 4.4.1 | ||
@@ -4,0 +9,0 @@ |
@@ -66,3 +66,3 @@ /// <reference types="node" /> | ||
*/ | ||
readonly closed: boolean; | ||
get closed(): boolean; | ||
/** | ||
@@ -69,0 +69,0 @@ * Connect (or reconnect) to an FTP server. |
@@ -7,3 +7,2 @@ "use strict"; | ||
const FtpContext_1 = require("./FtpContext"); | ||
const nullObject_1 = require("./nullObject"); | ||
const parseList_1 = require("./parseList"); | ||
@@ -360,3 +359,9 @@ const ProgressTracker_1 = require("./ProgressTracker"); | ||
// and removes the event listener for the source stream too early. | ||
return await transfer_1.upload(this.ftp, this._progressTracker, source, command, validPath); | ||
return await transfer_1.uploadFrom(source, { | ||
ftp: this.ftp, | ||
tracker: this._progressTracker, | ||
command, | ||
remotePath: validPath, | ||
type: "upload" | ||
}); | ||
} | ||
@@ -423,6 +428,11 @@ finally { | ||
await this.prepareTransfer(this.ftp); | ||
const command = startAt > 0 ? `REST ${startAt}` : `RETR ${validPath}`; | ||
// Keep the keyword `await` or the `finally` clause below runs too early | ||
// and removes the event listener for the source stream too early. | ||
return await transfer_1.download(this.ftp, this._progressTracker, destination, command, validPath); | ||
return await transfer_1.downloadTo(destination, { | ||
ftp: this.ftp, | ||
tracker: this._progressTracker, | ||
command: startAt > 0 ? `REST ${startAt}` : `RETR ${validPath}`, | ||
remotePath: validPath, | ||
type: "download" | ||
}); | ||
} | ||
@@ -466,4 +476,9 @@ finally { | ||
const writable = new StringWriter_1.StringWriter(); | ||
const noTracker = nullObject_1.createNullObject(); // Don't track progress of list transfers. | ||
await transfer_1.download(this.ftp, noTracker, writable, command); | ||
await transfer_1.downloadTo(writable, { | ||
ftp: this.ftp, | ||
tracker: this._progressTracker, | ||
command, | ||
remotePath: "", | ||
type: "list" | ||
}); | ||
const text = writable.getText(this.ftp.encoding); | ||
@@ -470,0 +485,0 @@ this.ftp.log(text); |
@@ -71,5 +71,5 @@ export declare enum FileType { | ||
constructor(name: string); | ||
readonly isDirectory: boolean; | ||
readonly isSymbolicLink: boolean; | ||
readonly isFile: boolean; | ||
get isDirectory(): boolean; | ||
get isSymbolicLink(): boolean; | ||
get isFile(): boolean; | ||
/** | ||
@@ -79,3 +79,4 @@ * Deprecated, legacy API. Use `rawModifiedAt` instead. | ||
*/ | ||
date: string; | ||
get date(): string; | ||
set date(rawModifiedAt: string); | ||
} |
@@ -76,3 +76,3 @@ /// <reference types="node" /> | ||
*/ | ||
readonly closed: boolean; | ||
get closed(): boolean; | ||
/** | ||
@@ -85,24 +85,27 @@ * Reset this contex and all of its state. | ||
*/ | ||
get socket(): Socket | TLSSocket; | ||
/** | ||
* Set the socket for the control connection. This will only close the current control socket | ||
* if the new one is not an upgrade to the current one. | ||
*/ | ||
socket: Socket | TLSSocket; | ||
* Set the socket for the control connection. This will only close the current control socket | ||
* if the new one is not an upgrade to the current one. | ||
*/ | ||
set socket(socket: Socket | TLSSocket); | ||
/** | ||
* Get the current FTP data connection if present. | ||
*/ | ||
get dataSocket(): Socket | TLSSocket | undefined; | ||
/** | ||
* Set the socket for the data connection. This will automatically close the former data socket. | ||
*/ | ||
dataSocket: Socket | TLSSocket | undefined; | ||
* Set the socket for the data connection. This will automatically close the former data socket. | ||
*/ | ||
set dataSocket(socket: Socket | TLSSocket | undefined); | ||
/** | ||
* Get the currently used encoding. | ||
*/ | ||
get encoding(): string; | ||
/** | ||
* Set the encoding used for the control socket. | ||
* | ||
* See https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings for what encodings | ||
* are supported by Node. | ||
*/ | ||
encoding: string; | ||
* Set the encoding used for the control socket. | ||
* | ||
* See https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings for what encodings | ||
* are supported by Node. | ||
*/ | ||
set encoding(encoding: string); | ||
/** | ||
@@ -130,3 +133,3 @@ * Send an FTP command without waiting for or handling the result. | ||
*/ | ||
readonly hasTLS: boolean; | ||
get hasTLS(): boolean; | ||
/** | ||
@@ -133,0 +136,0 @@ * Removes reference to current task and handler. This won't resolve or reject the task. |
@@ -80,3 +80,3 @@ "use strict"; | ||
get closed() { | ||
return this.socket.remoteAddress === undefined; | ||
return this.socket.remoteAddress === undefined || this._closingError !== undefined; | ||
} | ||
@@ -83,0 +83,0 @@ /** |
/// <reference types="node" /> | ||
import { Socket } from "net"; | ||
export declare type ProgressType = "upload" | "download" | "list"; | ||
/** | ||
@@ -10,3 +11,3 @@ * Describes progress of file transfer. | ||
/** The type of transfer, typically "upload" or "download". */ | ||
readonly type: string; | ||
readonly type: ProgressType; | ||
/** Transferred bytes in current transfer. */ | ||
@@ -37,3 +38,3 @@ readonly bytes: number; | ||
*/ | ||
start(socket: Socket, name: string, type: string): void; | ||
start(socket: Socket, name: string, type: ProgressType): void; | ||
/** | ||
@@ -40,0 +41,0 @@ * Stop tracking transfer progress. |
/// <reference types="node" /> | ||
import { Writable, Readable } from "stream"; | ||
import { FTPContext, FTPResponse } from "./FtpContext"; | ||
import { ProgressTracker } from "./ProgressTracker"; | ||
import { ProgressTracker, ProgressType } from "./ProgressTracker"; | ||
export declare type UploadCommand = "STOR" | "APPE"; | ||
@@ -26,11 +26,10 @@ /** | ||
export declare function connectForPassiveTransfer(host: string, port: number, ftp: FTPContext): Promise<void>; | ||
/** | ||
* Upload stream data as a file. For example: | ||
* | ||
* `upload(ftp, fs.createReadStream(localFilePath), remoteFilename)` | ||
*/ | ||
export declare function upload(ftp: FTPContext, progress: ProgressTracker, source: Readable, command: UploadCommand, remoteFilename: string): Promise<FTPResponse>; | ||
/** | ||
* Download data from the data connection. Used for downloading files and directory listings. | ||
*/ | ||
export declare function download(ftp: FTPContext, progress: ProgressTracker, destination: Writable, command: string, remoteFilename?: string): Promise<FTPResponse>; | ||
export interface TransferConfig { | ||
command: string; | ||
remotePath: string; | ||
type: ProgressType; | ||
ftp: FTPContext; | ||
tracker: ProgressTracker; | ||
} | ||
export declare function uploadFrom(source: Readable, config: TransferConfig): Promise<FTPResponse>; | ||
export declare function downloadTo(destination: Writable, config: TransferConfig): Promise<FTPResponse>; |
@@ -213,11 +213,6 @@ "use strict"; | ||
} | ||
/** | ||
* Upload stream data as a file. For example: | ||
* | ||
* `upload(ftp, fs.createReadStream(localFilePath), remoteFilename)` | ||
*/ | ||
function upload(ftp, progress, source, command, remoteFilename) { | ||
const resolver = new TransferResolver(ftp, progress); | ||
const fullCommand = `${command} ${remoteFilename}`; | ||
return ftp.handle(fullCommand, (res, task) => { | ||
function uploadFrom(source, config) { | ||
const resolver = new TransferResolver(config.ftp, config.tracker); | ||
const fullCommand = `${config.command} ${config.remotePath}`; | ||
return config.ftp.handle(fullCommand, (res, task) => { | ||
if (res instanceof Error) { | ||
@@ -227,3 +222,3 @@ resolver.onError(task, res); | ||
else if (res.code === 150 || res.code === 125) { // Ready to upload | ||
const dataSocket = ftp.dataSocket; | ||
const dataSocket = config.ftp.dataSocket; | ||
if (!dataSocket || !dataSocket.remoteAddress) { | ||
@@ -237,4 +232,4 @@ resolver.onError(task, new Error("Upload should begin but no data connection is available.")); | ||
onConditionOrEvent(canUpload, dataSocket, "secureConnect", () => { | ||
ftp.log(`Uploading to ${netUtils_1.describeAddress(dataSocket)} (${netUtils_1.describeTLS(dataSocket)})`); | ||
resolver.onDataStart(remoteFilename, "upload"); | ||
config.ftp.log(`Uploading to ${netUtils_1.describeAddress(dataSocket)} (${netUtils_1.describeTLS(dataSocket)})`); | ||
resolver.onDataStart(config.remotePath, config.type); | ||
source.pipe(dataSocket).once("finish", () => { | ||
@@ -255,8 +250,5 @@ dataSocket.destroy(); // Explicitly close/destroy the socket to signal the end. | ||
} | ||
exports.upload = upload; | ||
/** | ||
* Download data from the data connection. Used for downloading files and directory listings. | ||
*/ | ||
function download(ftp, progress, destination, command, remoteFilename = "") { | ||
if (!ftp.dataSocket) { | ||
exports.uploadFrom = uploadFrom; | ||
function downloadTo(destination, config) { | ||
if (!config.ftp.dataSocket) { | ||
throw new Error("Download will be initiated but no data connection is available."); | ||
@@ -266,5 +258,5 @@ } | ||
// receives the announcement. Start listening for data immediately. | ||
ftp.dataSocket.pipe(destination); | ||
const resolver = new TransferResolver(ftp, progress); | ||
return ftp.handle(command, (res, task) => { | ||
config.ftp.dataSocket.pipe(destination); | ||
const resolver = new TransferResolver(config.ftp, config.tracker); | ||
return config.ftp.handle(config.command, (res, task) => { | ||
if (res instanceof Error) { | ||
@@ -274,3 +266,3 @@ resolver.onError(task, res); | ||
else if (res.code === 150 || res.code === 125) { // Ready to download | ||
const dataSocket = ftp.dataSocket; | ||
const dataSocket = config.ftp.dataSocket; | ||
if (!dataSocket || !dataSocket.remoteAddress) { | ||
@@ -280,8 +272,8 @@ resolver.onError(task, new Error("Download should begin but no data connection is available.")); | ||
} | ||
ftp.log(`Downloading from ${netUtils_1.describeAddress(dataSocket)} (${netUtils_1.describeTLS(dataSocket)})`); | ||
resolver.onDataStart(remoteFilename, "download"); | ||
config.ftp.log(`Downloading from ${netUtils_1.describeAddress(dataSocket)} (${netUtils_1.describeTLS(dataSocket)})`); | ||
resolver.onDataStart(config.remotePath, config.type); | ||
onConditionOrEvent(destination.destroyed || destination.writableFinished, destination, "finish", () => resolver.onDataDone(task)); | ||
} | ||
else if (res.code === 350) { // Restarting at startAt. | ||
ftp.send("RETR " + remoteFilename); | ||
config.ftp.send("RETR " + config.remotePath); | ||
} | ||
@@ -297,3 +289,3 @@ else if (parseControlResponse_1.positiveCompletion(res.code)) { // Transfer complete | ||
} | ||
exports.download = download; | ||
exports.downloadTo = downloadTo; | ||
/** | ||
@@ -300,0 +292,0 @@ * Calls a function immediately if a condition is met or subscribes to an event and calls |
{ | ||
"name": "basic-ftp", | ||
"version": "4.4.1", | ||
"version": "4.5.0", | ||
"description": "FTP client for Node.js, supports explicit FTPS over TLS, IPv6, Async/Await, and Typescript.", | ||
@@ -39,12 +39,12 @@ "main": "dist/index", | ||
"devDependencies": { | ||
"@types/node": "12.11.7", | ||
"@typescript-eslint/eslint-plugin": "2.5.0", | ||
"@typescript-eslint/parser": "2.5.0", | ||
"@types/node": "12.12.6", | ||
"@typescript-eslint/eslint-plugin": "2.6.1", | ||
"@typescript-eslint/parser": "2.6.1", | ||
"eslint": "6.6.0", | ||
"js-yaml": ">=3.13.1", | ||
"mocha": "6.2.2", | ||
"mock-fs": "4.10.2", | ||
"mock-fs": "4.10.3", | ||
"rimraf": "3.0.0", | ||
"typescript": "3.6.4" | ||
"typescript": "3.7.2" | ||
} | ||
} |
@@ -168,3 +168,3 @@ # Basic FTP | ||
Set a callback function with `client.trackProgress` to track the progress of all uploads and downloads. To disable progress reporting, call `trackProgress` with an undefined handler. | ||
Set a callback function with `client.trackProgress` to track the progress of any transfer. Transfers are uploads, downloads or directory listings. To disable progress reporting, call `trackProgress` with an undefined handler. | ||
@@ -192,3 +192,3 @@ ```js | ||
For each transfer, the callback function will receive the filename, transfer type (upload/download) and number of bytes transferred. The function will be called at a regular interval during a transfer. | ||
For each transfer, the callback function will receive the filename, transfer type (`upload`, `download` or `list`) and number of bytes transferred. The function will be called at a regular interval during a transfer. | ||
@@ -237,10 +237,6 @@ There is also a counter for all bytes transferred since the last time `trackProgress` was called. This is useful when downloading a directory with multiple files where you want to show the total bytes downloaded so far. | ||
Set the encoding applied to all incoming and outgoing messages of the control connection. This encoding is also used when parsing a list response from a data connection. Node supports `utf8`, `latin1` and `ascii`. Default is `utf8` because it's backwards-compatible with `ascii` and many modern servers support it, some of them without mentioning it when requesting features. | ||
Set the encoding applied to all incoming and outgoing messages of the control connection. This encoding is also used when parsing a list response from a data connection. See https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings for what encodings are supported by Node.js. Default is `utf8` because most modern servers support it, some of them without mentioning it when requesting features. | ||
`get/set ipFamily` | ||
Set the preferred version of the IP stack: `4` (IPv4), `6` (IPv6) or `undefined` (Node.js default). Set to `undefined` by default. | ||
## Acknowledgment | ||
This library uses parts of the [directory listing parsers](https://github.com/apache/commons-net/tree/master/src/main/java/org/apache/commons/net/ftp/parser) written by The Apache Software Foundation. They've been made available under the Apache 2.0 license. See the [included notice](NOTICE.txt) and headers in the respective files containing the original copyright texts and a description of changes. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
140457
31
2888
239