パッケージの詳細

basic-ftp

patrickjuchli40mMIT5.0.5

FTP client for Node.js, supports FTPS over TLS, IPv6, Async/Await, and Typescript.

ftp, ftps, promise, async

readme

Basic FTP

npm version npm downloads Node.js CI

This is an FTP client library for Node.js. It supports FTPS over TLS, Passive Mode over IPv6, has a Promise-based API, and offers methods to operate on whole directories. Active Mode is not supported.

Advisory

Prefer alternative transfer protocols like HTTPS or SFTP (SSH). FTP is a an old protocol with some reliability issues. Use this library when you have no choice and need to use FTP. Try to use FTPS (FTP over TLS) whenever possible, FTP alone does not provide any security.

Dependencies

Node 10.0 or later is the only dependency.

Installation

npm install basic-ftp

Usage

The first example will connect to an FTP server using TLS (FTPS), get a directory listing, upload a file and download it as a copy. Note that the FTP protocol doesn't allow multiple requests running in parallel.

const { Client } = require("basic-ftp") 
// ESM: import { Client } from "basic-ftp"

example()

async function example() {
    const client = new Client()
    client.ftp.verbose = true
    try {
        await client.access({
            host: "myftpserver.com",
            user: "very",
            password: "password",
            secure: true
        })
        console.log(await client.list())
        await client.uploadFrom("README.md", "README_FTP.md")
        await client.downloadTo("README_COPY.md", "README_FTP.md")
    }
    catch(err) {
        console.log(err)
    }
    client.close()
}

The next example deals with directories and their content. First, we make sure a remote path exists, creating all directories as necessary. Then, we make sure it's empty and upload the contents of a local directory.

await client.ensureDir("my/remote/directory")
await client.clearWorkingDir()
await client.uploadFromDir("my/local/directory")

If you encounter a problem, it may help to log out all communication with the FTP server.

client.ftp.verbose = true

Client API

new Client(timeout = 30000)

Create a client instance. Configure it with a timeout in milliseconds that will be used for any connection made. Use 0 to disable timeouts, default is 30 seconds.

close()

Close the client and any open connection. The client can’t be used anymore after calling this method, you'll have to reconnect with access to continue any work. A client is also closed automatically if any timeout or connection error occurs. See the section on Error Handling below.

closed

True if the client is not connected to a server. You can reconnect with access.

access(options): Promise<FTPResponse>

Get access to an FTP server. This method will connect to a server, optionally secure the connection with TLS, login a user and apply some default settings (TYPE I, STRU F, PBSZ 0, PROT P). It returns the response of the initial connect command. This is an instance method and thus can be called multiple times during the lifecycle of a Client instance. Whenever you do, the client is reset with a new connection. This also implies that you can reopen a Client instance that has been closed due to an error when reconnecting with this method. The available options are:

  • host (string) Server host, default: localhost
  • port (number) Server port, default: 21
  • user (string) Username, default: anonymous
  • password (string) Password, default: guest
  • secure (boolean | "implicit") Explicit FTPS over TLS, default: false. Use "implicit" if you need support for legacy implicit FTPS.
  • secureOptions Options for TLS, same as for tls.connect() in Node.js.

features(): Promise<Map<string, string>>

Get a description of supported features. This will return a Map where keys correspond to FTP commands and values contain further details. If the FTP server doesn't support this request you'll still get an empty Map instead of an error response.

send(command): Promise<FTPResponse>

Send an FTP command and return the first response.

sendIgnoringError(command): Promise<FTPResponse>

Send an FTP command, return the first response, and ignore an FTP error response. Any other error or timeout will still reject the Promise.

cd(path): Promise<FTPResponse>

Change the current working directory.

pwd(): Promise<string>

Get the path of the current working directory.

list([path]): Promise<FileInfo[]>

List files and directories in the current working directory, or at path if specified. Currently, this library only supports MLSD, Unix and DOS directory listings. See FileInfo for more details.

lastMod(path): Promise<Date>

Get the last modification time of a file. This command might not be supported by your FTP server and throw an exception.

size(path): Promise<number>

Get the size of a file in bytes.

rename(path, newPath): Promise<FTPResponse>

Rename a file. Depending on the server you may also use this to move a file to another directory by providing full paths.

remove(path): Promise<FTPResponse>

Remove a file.

uploadFrom(readableStream | localPath, remotePath, [options]): Promise<FTPResponse>

Upload data from a readable stream or a local file to a remote file. If such a file already exists it will be overwritten. If a file is being uploaded, additional options offer localStart and localEndInclusive to only upload parts of it.

appendFrom(readableStream | localPath, remotePath, [options]): Promise<FTPResponse>

Upload data from a readable stream or a local file by appending it to an existing file. If the file doesn't exist the FTP server should create it. If a file is being uploaded, additional options offer localStart and localEndInclusive to only upload parts of it. For example: To resume a failed upload, request the size of the remote, partially uploaded file using size() and use it as localStart.

downloadTo(writableStream | localPath, remotePath, startAt = 0): Promise<FTPResponse>

Download a remote file and pipe its data to a writable stream or to a local file. You can optionally define at which position of the remote file you'd like to start downloading. If the destination you provide is a file, the offset will be applied to it as well. For example: To resume a failed download, request the size of the local, partially downloaded file and use that as startAt.


ensureDir(remoteDirPath): Promise<void>

Make sure that the given remoteDirPath exists on the server, creating all directories as necessary. The working directory is at remoteDirPath after calling this method.

clearWorkingDir(): Promise<void>

Remove all files and directories from the working directory.

removeDir(remoteDirPath): Promise<void>

Remove all files and directories from a given directory, including the directory itself. The working directory stays the same unless it is part of the deleted directories.

uploadFromDir(localDirPath, [remoteDirPath]): Promise<void>

Upload the contents of a local directory to the current remote working directory. This will overwrite existing files with the same names and reuse existing directories. Unrelated files and directories will remain untouched. You can optionally provide a remoteDirPath to put the contents inside any remote directory which will be created if necessary including all intermediate directories. The working directory stays the same after calling this method.

downloadToDir(localDirPath, [remoteDirPath]): Promise<void>

Download all files and directories of the current working directory to a given local directory. You can optionally set a specific remote directory. The working directory stays the same after calling this method.


trackProgress(handler)

Report any transfer progress using the given handler function. See the next section for more details.

Transfer Progress

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 without a handler.

// Log progress for any transfer from now on.
client.trackProgress(info => {
    console.log("File", info.name)
    console.log("Type", info.type)
    console.log("Transferred", info.bytes)
    console.log("Transferred Overall", info.bytesOverall)
})

// Transfer some data
await client.uploadFrom(someStream, "test.txt")
await client.uploadFrom("somefile.txt", "test2.txt")

// Set a new callback function which also resets the overall counter
client.trackProgress(info => console.log(info.bytesOverall))
await client.downloadToDir("local/path", "remote/path")

// Stop logging
client.trackProgress()

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.

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.

Error Handling

Any error reported by the FTP server will be thrown as FTPError. The connection to the FTP server stays intact and you can continue to use your Client instance.

This is different with a timeout or connection error: In addition to an Error being thrown, any connection to the FTP server will be closed. You’ll have to reconnect with client.access(), if you want to continue any work.

Logging

Using client.ftp.verbose = true will log debug-level information to the console. You can use your own logging library by overriding client.ftp.log. This method is called regardless of what client.ftp.verbose is set to. For example:

myClient.ftp.log = myLogger.debug

Static Types

In addition to unit tests and linting, the source code is written in Typescript using rigorous compiler settings like strict and noImplicitAny. When building the project, the source is transpiled to Javascript and type declaration files. This makes the library useable for both Javascript and Typescript projects.

Extending the library

Client

get/set client.parseList

Provide a function to parse directory listing data. This library supports MLSD, Unix and DOS formats. Parsing these list responses is one of the more challenging parts of FTP because there is no standard that all servers adhere to. The signature of the function is (rawList: string) => FileInfo[].

FTPContext

The Client API described so far is implemented using an FTPContext. An FTPContext provides the foundation to write an FTP client. It holds the socket connections and provides an API to handle responses and events in a simplified way. Through client.ftp you get access to this context.

get/set verbose

Set the verbosity level to optionally log out all communication between the client and the server.

get/set encoding

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.

Acknowledgment

This library uses parts of the directory listing parsers written by The Apache Software Foundation. They've been made available under the Apache 2.0 license. See the included notice and headers in the respective files containing the original copyright texts and a description of changes.

更新履歴

Changelog

5.0.5

  • Fixed: Memory leak described in #250 by @everhardt, @martijnimhoff

5.0.4

  • Fixed: Handle relative paths in Client.removeDir()

5.0.3

  • Fixed: Improve closing sequence for control socket. (#224, #228)
  • Fixed: Avoid deprecation error. (#221)
  • Fixed: Set default settings before login in case there are non-ascii characters in user or password. (#229, @lgpseu)

5.0.2

  • Fixed: Use existing code path to close control socket when replacing it.
  • Fixed: Destroy sockets explicitly on timeouts only.

5.0.1

  • Fixed: Don't trust FEAT response of servers regarding EPSV. (#213)
  • Fixed: Include original error message when client reports as closed.

5.0.0

  • Breaking change: Library requires at least Node version 10.
  • Changed: Use feature set as reported by server to determine transfer strategy.
  • Changed: Test suite improvements to better reflect behaviour of an FTP server.
  • Fixed: Issues around incomplete upload described in #205.
  • Fixed: Variation of empty directory listing described in #206.
  • Fixed: List parsing for specific Unix flavor described in #193.
  • Fixed: Prevent late internal exceptions from sockets.
  • Fixed: Stop explicitly destroying sockets, too early in rare cases.

4.6.6

  • Fixed: Log reason why transfer modes fail when testing.

4.6.5

  • Fixed: Only use MLSD after querying support for feature. (#187)

4.6.4

  • Fixed: Hostname doesn't match certificates altnames for data connection. (#166, #179, @alandoherty)

4.6.3

  • Improved: Continue trying transfer strategies even after unexpected errors. (#164)

4.6.2

  • Fixed: Allow directory names ending with space in client.list(). (#149, @inithink)
  • Fixed: Don't trim incoming message chunks.
  • Fixed: Reference Node.js types in tsconfig settings.

4.6.1

  • Fixed: Missing StringEncoding in export. (#144)

4.6.0

  • Added: Support for implicit FTPS. (#121, @sparebytes)
  • Fixed: Detection of stream being finished early. (#143)

4.5.4

  • Fixed: Catch server closing connection without error. (#138)

4.5.3

  • Fixed: Allow 'undefined' to be passed to trackProgress. (#125, @FabianMeul)

4.5.2

  • Fixed: Try next available list command after any FTP error. (#117)

4.5.1

  • Fixed: Remove eager check for remoteAddress of a socket. (#106)

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

  • Fixed: Return to former working directory also after error when calling directory-related methods.

4.4.0

  • Changed: Current API uploadDir and downloadDir has been deprecated, use uploadFromDir and downloadToDir.
  • Added: You can specifiy a custom remote directory with downloadToDir.

4.3.2

  • Fixed regression at 4.3.0: File descriptor closed too early. (#103)

4.3.1

  • Fixed: When downloading to a local file and an error occurs, only remove it if no data has been downloaded so far.

4.3.0

  • Added: More explicit API uploadFrom, appendFrom and downloadTo. upload and download are still available but deprecated.
  • Added: Handle file downloads and uploads directly by supporting local file paths in uploadFrom and downloadTo.
  • Added: Make it easier to resume a download of a partially downloaded file. See documentation of downloadTo for more details.

4.2.1

  • Fixed: Don't rely on MLSD types 'cdir' and 'pdir'. (#99)

4.2.0

  • Added: Support uploading a local directory to any specific remote directory instead of just the working directory.

4.1.0

  • Added: Support symbolic links in MLSD listings.

4.0.2

  • Fixed: Make MLSD listing detection more general. (#95)
  • Fixed: Handle MLSD facts 'sizd', 'UNIX.gid' and 'UNIX.uid'. (#95)

4.0.1

  • Fixed: Describe client as closed before first connection (#94)

4.0.0

This release contains the following breaking changes:

  • Changed: The permissions property of FileInfo is now undefined if no Unix permissions are present. This is the case if for example the FTP server does not actually run on Unix. Before, permissions would have been set to 000. If permissions are present there is a good chance that a command like SITE CHMOD will work for the current server.
  • Changed: MLSD is now the default directory listing command. If the connected server doesn't support it, the library will continue using the LIST command. This might have an impact on reported permissions for a file. It is possible although rare that a server running on Unix would have reported permissions with LIST but doesn't do so with MLSD.
  • Changed: If you've been parsing date of FileInfo, you might have to consider a new ISO format coming with MLSD listings, e.g. 2018-10-25T12:04:59.000Z. Better yet, use the parsed date directly with modifiedAt and only use date if it is undefined. Be aware that parsing dates reported by the LIST command is likely unreliable.

Non-breaking changes:

  • Added: Support for MLSD directory listing. This is a machine-readable directory listing format that provides modification dates that can be reliably parsed. Listings by the older command LIST have not been designed to be machine-readable and are notoriously hard to parse.
  • Added: The property modifiedAt of FileInfo may hold a parsed date if the FTP server supports the MLSD command. Note that the property date is not parsed but only a human-readable string coming directly from the original listing response.
  • Added: New API sendIgnoringError to send an FTP command and ignoring a resulting FTP error. Using the boolean flag as the second argument of send has been deprecated.
  • Added: Sending OPTS UTF8 ON when accessing a server.

3.8.3 - 3.8.7

No changes, republishing because of bug on npmjs.com.

3.8.2

  • Fixed: Fall back to LIST command if LIST -a is not supported. (#91)

3.8.1

  • Fixed: Support non-standard response to EPSV by IBM i or z/OS servers. (#87)
  • Fixed: Make unit tests for failing streams less dependent on platform. (#86)
  • Fixed: Improve marking protected methods for JS compilation output.

3.8.0

  • Added: Use client.append() to append to an existing file on the FTP server. (#83)

3.7.1

  • Fixed: Use ESLint instead of TSLint.

3.7.0

  • Added: Users can access internal transfer modes to force a specific one. (#77)
  • Fixed: Handle stream error events for upload and download.

3.6.0

  • Added: Make parseList public API. (#75, @xnerhu)
  • Changed: Update Typescript 3.5.1

3.5.0

  • Added: Client list method supports optional path argument. (#69, @ThatOdieGuy)
  • Changed: Updated Typescript 3.4.4

3.4.4

  • Fixed: Reject failing connection for passive transfer with Error instance. (#65)

3.4.3

  • Fixed: Handle multline response message closing without message. (#63)
  • Fixed: Track timeout during connect. (#64)

3.4.2

  • Fixed: Unix directory listing in some cases interpreted as DOS listing. (#61)

3.4.1

  • Fixed: Close the control connection when connect creates a new one.

3.4.0

  • Changed: access and connect can reopen a closed Client.
  • Fixed: access can be called again after failed login. (#56)

3.3.1

  • Fixed: Republish to (maybe) fix NPM issue of wrong stats.

3.3.0

  • Added: Support for leading whitespace in file and directory names.

3.2.2

  • Fixed: Make package scripts easier to understand.

3.2.1

  • Fixed: Republish to (maybe) fix NPM issue of wrong stats.

3.2.0

  • Changed: Source is now written in Typescript, fixes #49.

3.1.1

  • Fixed: Switch seamlessly between control and data connection for tracking timeout.

3.1.0

  • Added: Full type-checking as part of CI with Typescript and JSDoc type declarations. Check is rigourous, settings include 'strict' or 'noImplicitAny'.
  • Changed: Improved handling of unexpected server requests during transfer.

3.0.0

This release contains the following breaking changes:

  • Changed: Client is now single-use only. It can't be used anymore once it closes and a new client has to be instantiated.
  • Changed: All exceptions are now instances of Error, not custom error objects. Introduced FTPError for errors specific to FTP. (#37)

Non-breaking changes:

  • Added: If there is a socket error outside of a task, the following task will receive it. (#43)
  • Changed: Improved feedback if a developer forgets to use await or .then() for tasks. (#36)

Special thanks to @broofa for feedback and reviews.

2.17.1

  • Fixed: Multibyte UTF-8 arriving in multiple chunks (#38)
  • Fixed: Unit test throws unhandled exception (#44)
  • Fixed: Provide stack trace when closing due to multiple tasks running
  • Internal improvements to linting (@broofa)

2.17.0

  • Added: Get last modification time of a file. (#32, @AnsonYeung)

2.16.1

  • Fixed: Closing client during task will reject associated promise. (#34)

2.16.0

  • Changed: Include hidden files in file listings, fixes removeDir and clearWorkingDir. Changes behaviour for downloadDir and list. (#29, @conlanpatrek)

2.15.0

  • Changed: Timeout on control socket is only considered during active task, not when idle. This also fixes #27, as well as #26.

2.14.4

  • Fixed: Regression where closed clients throws timeout because of idle socket. (#26)

2.14.3

  • Fixed: JSDoc type annotations.
  • Fixed: Minor fixes to documentation.

2.14.2

  • Fixed: Unit test for adjusted behavior when closing context.

2.14.1

  • Fixed: Make it possible to reconnect after closing the FTP context.

2.14.0

  • Added: Improved error handling and reporting.

2.13.2

  • Fixed: Various improvements to documentation.

2.13.1

  • Fixed: Exception thrown if tasks will run in parallel because the user forget to use 'await'.
  • Fixed: Describe in documentation what exactly happens if there is a timeout.

2.13.0

  • Added: Use client.rename() to rename or move a file.
  • Changed: Default timeout set to 30 seconds.
  • Changed: Timeouts are tracked exlusively by data connection during transfer.
  • Fixed: Node's socket.removeAllListeners() doesn't work, see https://github.com/nodejs/node/issues/20923
  • Fixed: Node 8 is required, correct documentation and CI.

2.12.3

  • Fixed: Minor changes to documentation.

2.12.2

  • Fixed: Don't deny EPSV over IPv4. This can help in some circumstances with a NAT.

2.12.1

  • Fixed: Don't prefer IPv6 by default.

2.12.0

  • Added: Support IPv6 for passive mode (EPSV).
  • Added: Detect automatically whether to use EPSV or PASV.
  • Added: Log server IP when connected.

2.11.0

  • Added: Convenience method client.access to simplify access to an FTP(S) server.
  • Updated API documentation.
  • Stop using Yarn for internal dev-dependencies.

2.10.0

  • Added: Resolve simple NAT issues with PASV.
  • Added: Log socket encryption right before login.
  • Fixed: Remove obsolete socket connection error listener.

2.9.2

  • Improved documentation of client methods.
  • Fixed: Reason for error when parsing PASV response was not reported.

2.9.1

  • Mention regression in Node.js negatively affecting upload progress reporting.
  • Small fixes in documentation.

2.9.0

  • Added: Report transfer progress with client.trackProgress().
  • Added: Error return codes can be ignored when removing a single file.
  • Fixed: Timeout behaviour of control and data socket.

2.8.3

  • Improve introduction.
  • More unit tests.

2.8.2

  • When downloading, handle incoming data before announcement from control socket arrives.
  • More tests for uploading and downloading data including directory listings.
  • Use download mechanism for directory listings as well.

2.8.1

  • Improve documentation.
  • Update linter.

2.8.0

  • Change uploadDir() so that it reuses directories on the FTP server if they already exist. (#5)

2.7.1

  • Fix linter complaint.

2.7.0

  • Add method to remove a file.
  • Fix listing parser autodetection by filtering out empty lines.
  • Fix upload with TLS, wait for secureConnect if necessary.

2.6.2

  • Fix TLS upgrade for data connections. (#4)

2.6.1

  • Handle TLS upgrade error by reporting it to the current task handler.

2.6.0

  • Add method to retrieve file size.

2.5.2

  • Don't report unexpected positive completion codes as errors.
  • Improve documentation.

2.5.1

  • Mention DOS-style directory listing support in README.

2.5.0

  • Add support for DOS-style directory listing.
  • Select a compatible directory listing parser automatically.
  • Throw an exception with a detailed description if the directory listing can't be parsed.

2.4.2

  • Fix documentation of default arguments for login().

2.4.1

  • Improve introduction in README

2.4.0

  • Add default port for connect().
  • Add default anonymous credentials for login().
  • Improve documentation

2.3.3

  • Accept more positive preliminary and completion replies for transfers.

2.3.2

  • Documentation improvements
  • More internal functions made available for custom extensions.

2.3.1

  • Wait for both data and control connection reporting completion for list, upload and download.

2.3.0

  • Add features() method to client that returns a parsed result of the FEAT command.
  • Give access to internal list, upload and download functions for reuse in custom contexts.

2.2.1

  • Handle case when downloading, a server might report transfer complete when it isn't.
  • Document encoding property on FTPContext.

2.2.0

  • Encoding can be set explicitly, defaults to UTF-8.
  • Handle multiline responses arriving in multiple chunks.

2.1.0

  • Support multiline responses.
  • Get user access to some internal utility functions useful in custom contexts.

2.0.0

  • Complete redesign: Better separation between a simple object-oriented client, clear customization points and access to internals. Better discovery of features. This release is very much not backwards-compatible.

1.2.0

  • Add functions to upload, download and remove whole directories.
  • Add function to ensure a given remote path, creating all directories as necessary.

1.1.1

  • Differentiate between Basic API and Convenience API in documentation.

1.1.0

  • Add convenience functions to request and change the current working directory.
  • Return positive response results whenever reasonable.

1.0.9

  • Listeners using once wherever possible.

1.0.8

  • Fix result for send command.

1.0.7

  • Improve documentation.

1.0.6

  • Close sockets on timeout.

1.0.5

  • Close data socket explicitly after upload is done.

1.0.4

  • List: Some servers send confirmation on control socket before the data arrived.

1.0.3

  • List: Wait until server explicitly confirms that the transfer is complete.
  • Upload: Close data socket manually when a stream ended.

1.0.2

Initial release