Skip to content
⚡ An API wrapper around ssh2 and basic-ftp for building FTP/FTPS/SFTP clients.
TypeScript
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.github docs: create FUNDING.yml Oct 5, 2019
src
static
.gitignore
.prettierrc
.travis.yml
LICENSE
README.md
package-lock.json
package.json
tsconfig.json
tslint.json

README.md

Qusly-core


Travis NPM NPM Discord Github

Qusly-core is an API wrapper around ssh2 and basic-ftp for building FTP/FTPS/SFTP clients. It's used in Qusly.

Features

  • Supports FTP, FTPS, SFTP
  • Promises
  • Lots of utilities like createBlank
  • Splited transfer
  • Automatically calculates ETA and transfer speed
  • MS-dos support

Checkout roadmap to see what's coming.

Installing

$ npm install qusly-core

Quick start

An example of listing files:

import { Client } from 'qusly-core';

async function init() {
  const client = new Client();

  await client.connect({
    host: 'www.example.com',
    user: 'root',
    password: 'password'
    protocol: 'sftp',
    port: 22,
  });

  const files = await client.readDir('/documents');
  console.log(files);

  await client.disconnect();
}

init();

Example output:

[
  {
    name: 'projects',
    type: 'directory',
    size: 4096,
    ext: ''
    user: 'root',
    group: 'root',
    date: '2019-05-10T18:52:00.000Z', // obj Date
    permissions: {
      user: 6,
      group: 6
    },
  },
  {
    name: 'logs.txt',
    type: 'file',
    ext: 'txt'
    size: 43,
    user: 'root',
    group: 'root',
    date: '2019-05-29T22:00:00.000Z', // obj Date
    permissions: {
      user: 6,
      group: 6
    },
  },
]

API

Class Client:

Class TransferClient:

Interfaces:

Types:

Events:

Class Client

Methods

  • Client.abort(): Promise<void>
    Aborts current data transfer. It closes all used file streams.

    const bytes = await client.abort();
    
    console.log(`Aborted at ${bytes} bytes`);

  • Client.connect(config: IConfig): Promise<void>
    Connects to server. You can use it to reload session.

    try {
      await client.connect({
        host: 'www.example.com',
        user: 'root', // default anonymous
        password: 'password', // default @anonymous
        protocol: 'ftp', // default ftp
        port: 21, // default 21
      });
    
      console.log('Connected!');
    } catch (error) {
      console.log('Failed to connect!', res.error);
    }

  • Client.createBlank(type: 'folder' | 'file', path = './', files?: IFile[]): Promise<string>
    Creates an empty folder or file with unique name. If you've fetched files already, you can provide last argument to don't refetch files.

    const res = await client.createBlank('folder');
    
    console.log(`Created new folder - ${res.fileName}`);

  • Client.delete(path: string): Promise<void>
    An universal method to remove both files and folders.

    await client.delete('folder');
    
    console.log('Deleted');

  • Client.disconnect(): Promise<void>
    Disconnects from server. Closes all opened sockets and file streams.

    await client.disconnect();
    
    console.log('Disconnected!');

  • Client.download(path: string, dest: Writable, options?: IDownloadOptions): Promise<void>
    Downloads a file. You can start at given offset by setting options to

    {
      startAt: 65.536;
    }

    import { createWriteStream } from 'fs';
    import { resolve } from 'path';
    
    const localPath = resolve('downloads', 'downloaded.rar');
    
    client.on('progress', e => {
      const rate = ((e.buffered / e.size) * 100).toFixed(2);
    
      console.log(`${rate}% ETA: ${e.eta}s`);
    });
    
    await client.download('file.rar', createWriteStream(localPath));
    
    console.log('Downloaded!');

  • Client.exists(path: string): Promise<boolean>
    Checks if file exists.

    const exists = await client.exists('/home/image.png');
    
    if (exists) {
      console.log('File exists');
    } else {
      console.log("File doesn't exists");
    }

  • Client.mkdir(path: string): Promise<void>
    Creates a directory.

    await client.mkdir('/home/documents/new folder');
    
    console.log(`Created a new directory`);

  • Client.move(srcPath: string, destPath: string): Promise<void>
    Moves a file from srcPath to destPath.

    await client.move('music/film.mp4', 'videos/film.mp4');
    
    console.log('Moved');

  • Client.pwd(): Promise<IPwdRes>
    Returns path of current working directory.

    const path = await client.pwd();
    
    console.log(`You're at ${path}`);

  • Client.readDir(path?: string): Promise<IFile[]>
    Reads content of a directory. If you don't provide path, it'll use working directory.

    const files = await client.readDir('/root/');
    
    console.log(files);

  • Client.rimraf(path: string): Promise<void>
    Removes a directory and all of its content, recursively.

    await client.rimraf('videos');
    
    console.log('Removed all files');

  • Client.send(command: string): Promise<string>
    Sends a raw command. Output depends on a protocol and server support!

    // It'll probably work on SFTP
    const res = await client.send('whoami');
    
    console.log(res);

  • Client.size(path: string): Promise<number>
    Returns size of a file or folder in bytes.

    const size = await client.size('file.rar');
    
    console.log(`Size: ${size} bytes`);

  • Client.stat(path: string): Promise<IStats>
    Returns info about file at given path.

    const res = await client.stat('/documents/unknown');
    
    console.log(`${res.type} - ${res.size} bytes`);

  • Client.touch(path: string): Promise<IRes>
    Creates an empty file.

    await client.touch('./empty file.txt');
    
    console.log('Created a new file!');

  • Client.unlink(path: string): Promise<IRes>
    Removes a file at path.

    await client.unlink('videos/file.mp4');
    
    console.log('Removed a file');

  • Client.upload(path: string, source: Readable, options?: ITransferOptions): Promise<void>
    Uploads a file.

    import { createReadStream, statSync } from 'fs';
    import { resolve } from 'path';
    
    const localPath = resolve('uploads', 'file.jpg');
    const fileSize = statSync(localPath).size;
    
    client.on('progress', e => {
      const rate = ((e.buffered / e.size) * 100).toFixed(2);
    
      console.log(`${rate}% ETA: ${e.eta}s`);
    });
    
    await client.upload('uploaded file.jpg', createReadStream(path));
    
    console.log('Uploaded');

Class TransferClient

An utility class to split transfers.

Methods

  • TransferClient(type: ITransferType, splits = 1)

  • TransferClient.connect(config: IConfig): Promise<void>
    Connects clients to server.

    await client.connect({
      host: 'www.example.com',
    });
    
    console.log(`All clients are connected!`);

  • TransferClient.getSplits(): number
    Gets splits length.

  • TransferClient.setSplits(count: number, config?: IConfig): Promise<void>
    Sets splits. If you're setting more than you had, you must provide config. If you're setting less than you had, it will automatically close rest of client.

    const client = new TransferClient('download', 2);
    
    console.log(client.getSplits()); // 2
    
    await client.setSplits(6, {
      host: 'www.example.com',
    });
    
    console.log(client.getSplits()); // 6
    
    await client.setSplits(4);
    
    console.log(client.getSplits()); // 4

  • TransferClient.transfer(localPath: string, remotePath: string, id?: string): Promise<void>
    Transfers a file. You can set your own id or it'll be unique hash. To track progress, use event progress. With 2 splits, you can transfer files twice as fast.

    await client.transfer('logs.txt', '/documents/logs.txt');
    await client.transfer('video.mp4', '/content/video.mp4');

Interface IConfig

interface IConfig {
  protocol?: IProtocol;
  host?: string;
  port?: number;
  user?: string;
  password?: string;
}

Interface IDownloadOptions

interface IDownloadOptions extends ITransferOptions {
  startAt?: number;
}

Interface IFile

interface IFile {
  name?: string;
  type?: IFileType;
  size?: number;
  user?: string;
  group?: string;
  date?: Date;
  ext?: string;
  permissions?: {
    user?: number;
    group?: number;
  };
}

Interface IProgress

interface IProgress {
  chunkSize?: number; // single chunk size in bytes
  buffered?: number; // already buffered size in bytes
  size?: number; // file size in bytes
  localPath?: string; // local file path
  remotePath?: string; // remote file path
  eta?: number; // estimated time arrival in seconds
  speed?: number; // transfer speed in bytes/s
  startAt?: Date; // transfer start
  percent?: number; // percent of buffered size
  context?: Client;
}

Interface IStats

interface IStats {
  size?: number;
  type?: IFileType;
}

Interface ITransferClientNew

interface ITransferClientNew {
  id?: string;
  type?: ITransferType;
  localPath?: string;
  remotePath?: string;
  context?: Client;
}

Interface ITransferClientProgress

interface ITransferClientProgress extends IProgress {
  id?: string;
  type?: ITransferType;
}

Interface ITransferOptions

interface ITransferOptions {
  quiet?: boolean;
}

Type IFileType

type IFileType = 'unknown' | 'file' | 'folder' | 'symbolic-link';

Type IProtocol

type IProtocol = 'sftp' | 'ftp' | 'ftps';

Type ITransferType

type ITransferType = 'download' | 'upload';

Events

Client

  • Client.on('abort') - File transfer has been aborted.

  • Client.on('connect') - Client has connected to server.

  • Client.on('disconnect') - Client has disconnected from server.

  • Client.on('progress', e: IProgress) - Triggered while transfering a file.

    client.on('progress', (e: IProgress) => {
      const { buffered, size, eta, speed } = data;
      const percent = (bytes / size * 100).toFixed(2);
    
      console.log(`${buffered}/${size}, ETA: ${eta}s, speed: ${speed}KB/s`);
    });
    
    client.download(...);

TransferClient

  • TransferClient.on('new', e: ITransferClientNew) - Invoked on every new file transfer.

  • TransferClient.on('progress', e: ITransferClientProgress) - Triggered while transfering a file. This event comes with a lot of information. Some of that are:
    • Id, which you can use to identify transfer.
    • Type of transfer ITransferType
    • Single chunk size in bytes
    • Buffered size
    • File size
    • Estimated time arrival in seconds
    • Transfer speed in bytes/s
    • Percent of buffered size
    • Start time
client.on('progress', (e: ITransferClientProgress) => {
  const { id, type, eta } = data;

  console.log(`${id}: ${eta}s (${type}`);
});

client.transfer(...);

Related

  • Qusly - Elegant, full-featured FTP client.
You can’t perform that action at this time.