Node.js ci permette di interagire con un server FTP in modo molto semplice.

Sostanzialmente tale interazione tra client (Node) e server (servizio FTP in uso sul server) avviene inviando dei comandi FTP ed interpretando le risposte restituite dal server.

A differenza di PHP, il supporto al protocollo FTP in Node.js avviene attraverso un modulo specifico e non con un'estensione. Quindi per prima cosa dobbiamo installare un modulo specifico.

npm install basic-ftp --save

A questo punto possiamo implementare una classe di base che esegue l'upload dei file sul server remoto e ne modifica i permessi.

'use strict';

const ftp = require('basic-ftp');
const fs = require('fs');

class FTPClient {
    constructor(host = 'localhost', port = 21, username = 'anonymous', password = 'guest', secure = false) {
        this.client = new ftp.Client();
        this.settings = {
            host: host,
            port: port,
            user: username,
            password: password,
            secure: secure
        };
    }

    upload(sourcePath, remotePath, permissions) {
        let self = this;
        (async () => {
            try {
                let access = await self.client.access(self.settings);
                let upload = await self.client.upload(fs.createReadStream(sourcePath), remotePath);
                let permissions = await self.changePermissions(permissions.toString(), remotePath);
            } catch(err) {
                console.log(err);
            }
            self.client.close();
        })();
    }

    close() {
        this.client.close();
    }

    changePermissions(perms, filepath) {
        let cmd = 'SITE CHMOD ' + perms + ' ' + filepath;
        return this.client.send(cmd, false);
    }
}

module.exports = FTPClient;

Per capirne il funzionamento dobbiamo tenere presente che una sessione FTP tra un client (in questo caso Node.js) e il server (il servizio FTP remoto gestito da un applicativo come ProFTPD, vsftpd eccetera) è appunto uno scambio di comandi e risposte da parte del server.

Dopo aver effettuato il login sull'host specificato fornendo username e password, si comincia a dare una serie di comandi al server che in Node vengono appunto implementati tramite i metodi del modulo che stiamo utilizzando.

La parte su cui spesso si fa confusione e che genera più in errori in assoluto sono i percorsi (path) sul server.

Esistono due tipi di percorsi sui server: relativi e assoluti. Quelli assoluti iniziano con uno slash (/), quelli relativi lo omettono.

Immaginiamo che un account utente sia /home/utente e che la directory principale del sito sia /home/utente/public_html. Se la directory FTP coincide con quella utente, quando lanciamo una sessione FTP e ci autentichiamo, la nostra directory corrente sarà /home/utente.

Quindi un comando come:

mkdir public_html/images

usa un percorso relativo e funzionerà creando la directory specificata sotto public_html. Ma se avessimo scritto:

mkdir /public_html/images

il comando sarebbe stato interpretato come un tentativo di accedere ad una directory che non si trova nella directory utente ma addirittura alla base stessa dell'intero filesystem del server, generando ovviamente un errore.

Il modulo basic-ftp invia direttamente i comandi FTP al server tramite il metodo send(). Il parametro booleano di questo metodo abilita o disabilita l'output degli errori nel risultato restituito. Tutti i metodi di questo modulo restituiscono Promise, quindi per semplificare la scrittura del codice abbiamo usato il modello async/await.

Esempio d'uso:

'use strict';

const ftp = require('./FTPClient');
const client = new ftp('192.168.1.4', 21, 'username', 'password', false);

client.upload('./upload.txt', 'public_html/upload.txt', 755);

In questo caso abbiamo caricato sul server un file di testo presente nella directory corrente nella directory remota public_html e ne abbiamo modificato i permessi in 755.

Modificare i permessi è spesso una pratica raccomandata quando non sappiamo quali siano i permessi predefiniti applicati dal server FTP ai vari formati di file.