Tutorial > Come usare Node.js per copiare file via SCP

Come usare Node.js per copiare file via SCP

Pubblicato il: 04 gennaio 2021

Node.js Storage Sviluppo

Introduzione

Copiare file da una macchina a un'altra è un’operazione piuttosto semplice quando si ha accesso fisico ad entrambe le macchine. Qualora tu stia utilizzando un Cloud Server, e non avessi quindi fisicamente accesso alla macchina sulla quale vuoi inviare una copia dei tuoi file, la maniera più sicura per effettuare questo tipo di trasferimento è sicuramente una copia mediante il protocollo SSH. 

Secure Copy Protocol (SCP) è un protocollo che sfrutta questo tipo di connessione, garantendo integrità e sicurezza nell’invio dei file da una sorgente al destinatario. Oltre a poter effettuare questo tipo di copia mediante semplice shell, molti linguaggi di programmazione forniscono moduli e librerie per includere questa operazione all’interno dei propri progetti.

In questo tutorial vedrai come effettuare l’invio e la ricezione di file e cartelle in Node.JS, indipendentemente dal sistema operativo della macchina sorgente e destinataria.

Per procedere dovrai aver già installato sul tuo sistema il framework Node.js. Qualora non l'avessi ancora fatto e utilizzassi un server Linux Ubuntu, puoi seguire la nostra guida su Come installare Node.js su Ubuntu 18.04.

Installazione moduli Node.js

Il trasferimento via SCP in Node.JS è possibile grazie al modulo “node-scp”installabile mediante il Packet Manager di Node. Apri quindi il terminale e procedi alla sua installazione mediante il comando:

npm i node-scp

Se per collegarti in SSH al dispositivo destinatario hai bisogno di indicare anche la chiave privata, avrai bisogno di installare un ulteriore modulo che ti servirà per far inviare anche la tua chiave “.pem” dallo script. In tal caso, procedi con l’installazione del modulo “fs” tramite:

npm i fs

Definizione server remoto

All’interno del tuo progetto inizia con il definire tutti i parametri che utilizzerai per collegarti al destinatario come se ti collegassi via SSH.

const scp = require('node-scp')
//var fs = require('fs');
var remote_server = {
     host: 'your host',  //remote host ip 
     port: 22, //port used for scp 
     username: 'username', //username to authenticate
     password: 'password', //password to authenticate
    // forceIPv4: boolean,    //Connection allow only via resolved IPv4 address (true/false)
    // forceIPv6: boolean,   //Connection allow only via resolved IPv6 address (true/false)
    // privateKey: fs.readFileSync('./key.pem'),
    // passphrase: 'your key passphrase', 
}

La variabili scp ed fs rappresentano in Node.JS una istanziazione delle librerie appena installate. Mediante queste variabili sarà infatti possibile accedere ai metodi messi a disposizione dalle librerie, rispettivamente per inviare/ricevere file e per fare accesso ai file salvati sul client.

L’oggetto “remote_server” rappresenta invece l’host destinatario da dover interrogare e, nello specifico:

  • host: indirizzo IP dell’host destinatario;
  • port: numero della porta sulla quale collegarsi (di default per la connessione ssh/scp viene usata la porta 22);
  • username: username dell’utente per effettuare il login sull’host destinatario;
  • password: password dell’utente per effettuare il login sull’host destinatario;
  • forceIPv4 (OPZIONALE): nel caso in cui fosse necessario indicare la connessione mediante solo IPv4 impostare tale parametro a “true”;
  • forceIPv6 (OPZIONALE): nel caso in cui fosse necessario indicare la connessione mediante solo IPv6 impostare tale parametro a “true”;
  • privateKey (OPZIONALE): nel caso in cui fosse necessario indicare la chiave per la connessione al destinatario, decommenta questa riga e passa come argomento della funzione “fs.readFileSync” il percorso del file “key.pem” presente nell’host sorgente;
  • passphrase (OPZIONALE): nel caso in cui fosse necessario indicare la passphrase per il login all’host destinatario, decommenta questa riga e indica la passphrase come stringa di tale attributo.

Promise vs Async-Await

Prima di procedere con il mostrare le diverse tipologie di invio e ricezione di file e cartelle, è fondamentale capire la differenza tra i concetti di “Promise” e di “Async-Await”.

Inviare files con Promise

Mediante il seguente snippet di codice, utilizzando il meccanismo delle Promise, potrai inviare il file (il cui percorso è definito nella variabile “local_file_path”) sull’host destinatario salvandolo nel percorso indicato nella variabile “destination_file_path”.

var local_file_path = './test.txt';
var destination_file_path = '/workspace/test.txt';

send_file_using_promise(local_file_path, destination_file_path);

function send_file_using_promise(file_path, destination_path){
    scp(remote_server).then(client => {
        client.uploadFile(file_path, destination_path)
              .then(response => {
                client.close()
              })
              .catch(error => {})
      }).catch(e => console.log(e))
}

La funzione “send_file_using_promise” tenterà una connessione al server precedentemente definito (remote_server) e in caso di riuscita effettuerà l’upload del file sul percorso prestabilito.

Al termine dell’invio chiuderà la connessione e rilascerà il comando a Node.JS per l’esecuzione di eventuali ulteriori funzioni previste.

Inviare files con Async/Await

Analogamente a quanto descritto prima, anche questa porzione di codice effettuerà l’upload del file specificato nella variabille local_file_path sull’host “remote_server” nel percorso indicato nella variabile “detination_file_path”. 

var local_file_path = './test.txt';
var detination_file_path = '/workspace/test.txt'; send__fileusing_async_await(local_file_path, detination_file_path); async function send_file_using_async_await(file_path, destination_path){
    try {
        const c = await scp(remote_server)
        await c.uploadFile(file_path, destination_path)
        c.close()
      } catch (e) {
        console.log(e)
      }
}

In questo caso però l’operazione avviene in maniera Asincrona, la cui logica viene comunque temporizzata mediante gli “await” posti all’interno della funzione “send_file_using_async_await”.

Scaricare file con Promise

Per scaricare invece il file con Promise utilizziamo il seguente codice: 

var local_file_path = './test.txt';
var detination_file_path = '/workspace/test.txt';
download_file_using_promise(detination_file_path, local_file_path);
function download_file_using_promise(file_path, destination_path){
    scp(remote_server).then(client => {
        client.downloadFile(file_path, destination_path)
              .then(response => {
                client.close()
              })
              .catch(error => {})
      }).catch(e => console.log(e))
}

Il protocollo SCP è bidirezionale: così come è possibile effettuare l’upload di un file su un destinatario è possibile effettuare il download dello stesso da una macchina sul proprio client. Le macchine quindi si scambiano i ruoli di sorgente e destinatario e questo cambio trova rappresentazione nella sostituzione del metodo “client.uploadFile” con “client.downloadFile”.

La logica del flusso resta invariata con la differenza che il file “text.txt” non viene più inviato dalla tua macchina verso quello che prima era l’host destinatario bensì viene acquisito sulla tua macchina da quest’ultimo. Da notare come i parametri passati alla funzione risultino invertiti rispetto alla funzione precedente.

Scaricare file con Async/Await

In maniera similare avviene il download del file appena mostrato mediante async-await:

var local_file_path = './test.txt';
var detination_file_path = '/workspace/test.txt';
download_file_using_async_await(detination_file_path, local_file_path);
async function download_file_using_async_await(file_path, destination_path){
    try {
        const client = await scp(remote_server)
        await client.downloadFile(file_path, destination_path)
        client.close()
      } catch(e) {
        console.log(e)
      }
}

L’invocazione della funzione “download_file_using_async_await” tenterà la connessione al client, aspetterà il rilascio del comando dato dall’avvenuta connessione allo stesso ed effettuerà il download del file nel percorso indicato nel secondo parametro passato alla funzione. Una volta scaricato il file verrà salvato nel percorso indicato nella variabile “local_file_path”.

Caricare cartelle

Analogamente ai file, é possibile utilizzare scp per trasferire intere cartelle da un macchina all'altra. 

Nei seguenti esempi ti mostriamo una implementazione di esempio per il caricamento di cartelle, utilizzando sia Promise sia Async/Await:

Promise

var local_folder_path = './local/dir';
var detination_folder_path = '/server/path';
        
send_folder_using_promise(local_folder_path, detination_folder_path);

function send_folder_using_promise(folder_path, destination_path){
    scp(remote_server).then(client => {
        client.uploadDir(folder_path, destination_path)
              .then(response => {
                client.close()
              })
              .catch(error => {})
      }).catch(e => console.log(e))
}

Async/Await

var local_folder_path = './local/dir';
var detination_folder_path = '/server/path';
        
send_folder_using_async_await(local_folder_path, detination_folder_path);

async function send_folder_using_async_await(folder_path, destination_path)
{
    try {
        const client = await scp(remote_server)
        await client.uploadDir(folder_path, destination_path)
        client.close()
      } catch (e) {
        console.log(e)
      }
}

Verificare l'esistenza di un percorso

Vediamo infine come verificare se sulla nostra macchina remota esista o meno un determinato percorso.

Questa funzionalità potrebbe tornarti molto utile qualora avessi bisogno di controllare l'effettivo caricamento di file o di cartelle. Potresti quindi usarla come una verifica finale del tuo script, provando a ritentare il caricamento se ci dovesse essere stato qualche errore durante il trasferimento.

Promise

var path_to_be_checked = '/server/path';
        
check_if_path_extists_using_promise(path_to_be_checked);

function check_if_path_extists_using_promise(path_to_check){
    scp(remote_server).then(client => {
        client.exists(path_to_check)
              .then(response => {
                client.close() 
              })
              .catch(error => {})
      }).catch(e => console.log(e))
}

Async/Await

var path_to_be_checked = '/server/path';
        
check_if_path_extists_using_async_await(path_to_be_checked);

async function check_if_path_extists_using_async_await(path_to_check){
    try {
        const client = await scp(remote)
        await client.exists(path_to_check)
        client.close()
      } catch (e) {
        console.log(e)
      }
}