Rimuovere la directory che non è vuota


300

Nella mia applicazione Node ho bisogno di rimuovere una directory che ha alcuni file, ma fs.rmdirfunziona solo su directory vuote. Come posso fare questo?


1
In breve: fs.readdir(dirPath)per una matrice di percorsi in una cartella, scorrere fs.unlink(filename)per eliminare ogni file e infine fs.rmdir(dirPath)eliminare la cartella ora vuota. Se è necessario ricorrere, controllare fs.lstat(filename).isDirectory().
iono,

Risposte:


319

Esiste un modulo per questo chiamato rimraf( https://npmjs.org/package/rimraf ). Fornisce le stesse funzionalità dirm -Rf

Utilizzo asincrono :

var rimraf = require("rimraf");
rimraf("/some/directory", function () { console.log("done"); });

Sincronizza utilizzo:

rimraf.sync("/some/directory");

1
Strano, non ho mai visto comportamenti del genere. Suggerirei di cercare e / o archiviare un bug. github.com/isaacs/rimraf/issues
Morgan ARR Allen

35
Questo è qualcosa che può essere fatto facilmente con le librerie NodeJS Core perché installare un pacchetto di terze parti non mantenuto?
SudoKid,

4
@EmettSpeer Quando intendi "fare facilmente"? Auto-scrittura di una funzione come deleteFolderRecursivenella seguente risposta?
Freewind,

23
"ma anche con la funzione sottostante è meglio quindi aggiungere un pacchetto non necessario al tuo sistema." Sono fortemente in disaccordo. Stai reinventando la ruota per la 19esima volta senza alcun motivo e stai rischiando di introdurre bug o vulnerabilità della sicurezza nel processo. Almeno è una perdita di tempo. Inb4 "che cosa succede se rilasciano il pacchetto": nel caso estremamente improbabile che il pacchetto venga rimosso dal registro npm, puoi sempre sostituirlo con il tuo . Non ha senso fasciare la testa prima di romperla.
Demonblack,

3
ora è possibile utilizzare recursiveun'opzione: stackoverflow.com/a/57866165/6269864

245

Per rimuovere la cartella in modo sincrono

const fs = require('fs');
const Path = require('path');

const deleteFolderRecursive = function(path) {
  if (fs.existsSync(path)) {
    fs.readdirSync(path).forEach((file, index) => {
      const curPath = Path.join(path, file);
      if (fs.lstatSync(curPath).isDirectory()) { // recurse
        deleteFolderRecursive(curPath);
      } else { // delete file
        fs.unlinkSync(curPath);
      }
    });
    fs.rmdirSync(path);
  }
};

33
Potrebbe voler aggiungere alcuni controlli che non eseguirai accidentalmente su "/". Ad esempio, se viene passato un percorso vuoto e un errore di battitura nel file potrebbe risultare curPath come directory principale.
Jake_Howard,

10
Implementazione più robusta: sostituisci var curPath = path + "/" + file;con var curPath = p.join(path, file);purché tu abbia incluso il modulo path:var p = require("path")
Andry

9
Windows ha \ barre, quindi path.join(dirpath, file)dovrebbe essere migliore dipath + "/" + file
thybzi il

5
Con questo codice potresti ottenere "Dimensione massima dello stack di chiamate superata" a causa di troppe operazioni in un solo tick. @Walf se si esegue l'applicazione console, si dispone di 1 client, non di più. Quindi, in questo caso, non è necessario utilizzare asincrono per l'app console
Leonid Dashko,

4
Ricevo "Errore: ENOTEMPTY: directory non vuota"
Seagull

168

La maggior parte delle persone che usano fsNode.js preferiscono funzioni simili al "modo Unix" di gestire i file. Sto usando fs-extra per portare tutte le cose interessanti:

fs-extra contiene metodi che non sono inclusi nel pacchetto vanilla Node.js fs. Come mkdir -p, cp -r e rm -rf.

Ancora meglio, fs-extra è un calo in sostituzione di fs nativo. Tutti i metodi in fs non sono modificati e collegati ad esso. Significa che puoi sostituire fs con fs-extra :

// this can be replaced
const fs = require('fs')

// by this
const fs = require('fs-extra')

E quindi puoi rimuovere una cartella in questo modo:

fs.removeSync('/tmp/myFolder'); 
//or
fs.remove('/tmp/myFolder', callback);

per la versione di sincronizzazione di cui hai bisognoremoveSync('/tmp/myFolder')
olidem

148

A partire dal 2019 ...

A partire da Node.js 12.10.0 , fs.rmdirSyncsupporta un'opzionerecursive , quindi puoi finalmente fare:

fs.rmdirSync(dir, { recursive: true });

Dove l' recursiveopzione elimina l'intera directory in modo ricorsivo.


5
@anneb Succede se si utilizza una versione precedente di Node.js (<12.10). L'ultima versione riconosce l'opzione recursive: trueed elimina le cartelle non vuote senza lamentele.
GOTO 0

9
La rimozione ricorsiva è ancora sperimentale a partire dal nodo v13.0.1
Tim

5
La firma della funzione è in realtà fs.rmdir(path[, options], callback)ofs.rmdirSync(path[, options])
conceptdeluxe il

@Tim cosa intendi con sperimentale?
Emerica,

2
@Emerica Nei documenti ufficiali di node.js c'è una grande notifica arancione che dice che fs.rmdirè sperimentale con stabilità 1. "Stabilità: 1 - Sperimentale. La funzione non è soggetta alle regole di versione semantica. In qualsiasi versione futura. L'uso della funzione non è raccomandato negli ambienti di produzione. "
Tim

24

La mia risposta modificata da @oconnecp ( https://stackoverflow.com/a/25069828/3027390 )

Utilizza path.join per una migliore esperienza multipiattaforma. Quindi, non dimenticare di richiederlo.

var path = require('path');

Anche rinominata funzione in rimraf;)

/**
 * Remove directory recursively
 * @param {string} dir_path
 * @see https://stackoverflow.com/a/42505874/3027390
 */
function rimraf(dir_path) {
    if (fs.existsSync(dir_path)) {
        fs.readdirSync(dir_path).forEach(function(entry) {
            var entry_path = path.join(dir_path, entry);
            if (fs.lstatSync(entry_path).isDirectory()) {
                rimraf(entry_path);
            } else {
                fs.unlinkSync(entry_path);
            }
        });
        fs.rmdirSync(dir_path);
    }
}

17

Di solito non riesco a resuscitare vecchi thread, ma qui c'è molto da sfornare e senza la risposta del rimraf, questi mi sembrano eccessivamente complicati.

Innanzitutto nel nodo moderno (> = v8.0.0) è possibile semplificare il processo utilizzando solo moduli core del nodo, completamente asincroni, e parallelizzare lo scollegamento dei file contemporaneamente in una funzione di cinque righe e mantenere comunque la leggibilità:

const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
const readdir = promisify(fs.readdir);
const rmdir = promisify(fs.rmdir);
const unlink = promisify(fs.unlink);

exports.rmdirs = async function rmdirs(dir) {
  let entries = await readdir(dir, { withFileTypes: true });
  await Promise.all(entries.map(entry => {
    let fullPath = path.join(dir, entry.name);
    return entry.isDirectory() ? rmdirs(fullPath) : unlink(fullPath);
  }));
  await rmdir(dir);
};

In un'altra nota, una guardia per gli attacchi di attraversamento del percorso è inappropriata per questa funzione perché

  1. È al di fuori dell'ambito di applicazione in base al principio della responsabilità singola .
  2. Dovrebbe essere gestito dal chiamante non questa funzione. Questo è simile alla riga di comando rm -rfin quanto accetta un argomento e consentirà all'utente rm -rf /se richiesto. Sarebbe responsabilità di uno script non proteggere il rmprogramma stesso.
  3. Questa funzione non sarebbe in grado di determinare un simile attacco poiché non ha un quadro di riferimento. Ancora una volta questa è la responsabilità del chiamante che avrebbe il contesto di intenti che gli fornirebbe un riferimento per confrontare il percorso.
  4. SYM-link non sono un problema, come .isDirectory()è falseper SYM-link e sono scollegati non ricorsivo in.

Ultimo ma non meno importante, esiste una rara condizione che la ricorsione potrebbe errori se una delle voci fosse scollegata o eliminata al di fuori di questo script al momento giusto mentre questa ricorsione è in esecuzione. Poiché questo scenario non è tipico nella maggior parte degli ambienti, può essere trascurato. Tuttavia, se necessario (per alcuni casi limite) questo problema può essere mitigato con questo esempio leggermente più complesso:

exports.rmdirs = async function rmdirs(dir) {
  let entries = await readdir(dir, { withFileTypes: true });
  let results = await Promise.all(entries.map(entry => {
    let fullPath = path.join(dir, entry.name);
    let task = entry.isDirectory() ? rmdirs(fullPath) : unlink(fullPath);
    return task.catch(error => ({ error }));
  }));
  results.forEach(result => {
    // Ignore missing files/directories; bail on other errors
    if (result && result.error.code !== 'ENOENT') throw result.error;
  });
  await rmdir(dir);
};

EDIT: crea isDirectory()una funzione. Rimuovere la directory effettiva alla fine. Correzione della ricorsione mancante.


1
Questa è una soluzione davvero accurata. Domanda relativa al secondo esempio di codice: non si chiama awaitil proprio Promise.all(…); è intenzionale? Sembra che nel suo stato attuale results.forEachripeterebbe le promesse, mentre il codice prevede di ripetere i risultati. Mi sto perdendo qualcosa?
Anton Strogonoff,

@Tony hai ragione è un errore di battitura / bug. Buona pesca!
Sukima,

Forse un controllo prima per assicurarsi che la directory esista? qualcosa del genereif (!fs.existsSync(dir)) return
GTPV,

@GTPV Perché? Ciò aumenta la responsabilità di questa funzione. readdirgenererà un errore come dovrebbe. Se rmdir non-existing-diril codice di uscita è un errore. Sarebbe responsabilità del consumatore provare / catturare. Questo è lo stesso metodo descritto nei documenti Node quando si tratta di usare le funzioni fs. Si aspettano che tu provi / catturi e osservi gli errori codeper determinare cosa fare. Un controllo extra introduce una condizione di gara.
Sukima,

Sicuramente vedo il tuo punto. Mi aspetterei però intuitivamente che il tentativo di eliminare una cartella che non esiste avrebbe esito positivo in quanto semplicemente non farebbe nulla. Nessuna condizione di competizione se fs.existsviene utilizzata la versione sincrona di . PS questa è un'ottima soluzione.
GTPV,

12

Ecco una versione asincrona della risposta di @ SharpCoder

const fs = require('fs');
const path = require('path');

function deleteFile(dir, file) {
    return new Promise(function (resolve, reject) {
        var filePath = path.join(dir, file);
        fs.lstat(filePath, function (err, stats) {
            if (err) {
                return reject(err);
            }
            if (stats.isDirectory()) {
                resolve(deleteDirectory(filePath));
            } else {
                fs.unlink(filePath, function (err) {
                    if (err) {
                        return reject(err);
                    }
                    resolve();
                });
            }
        });
    });
};

function deleteDirectory(dir) {
    return new Promise(function (resolve, reject) {
        fs.access(dir, function (err) {
            if (err) {
                return reject(err);
            }
            fs.readdir(dir, function (err, files) {
                if (err) {
                    return reject(err);
                }
                Promise.all(files.map(function (file) {
                    return deleteFile(dir, file);
                })).then(function () {
                    fs.rmdir(dir, function (err) {
                        if (err) {
                            return reject(err);
                        }
                        resolve();
                    });
                }).catch(reject);
            });
        });
    });
};

10

Ho scritto questa funzione chiamata Rimuovi cartella. Rimuoverà in modo ricorsivo tutti i file e le cartelle in una posizione. L'unico pacchetto richiesto è asincrono.

var async = require('async');

function removeFolder(location, next) {
    fs.readdir(location, function (err, files) {
        async.each(files, function (file, cb) {
            file = location + '/' + file
            fs.stat(file, function (err, stat) {
                if (err) {
                    return cb(err);
                }
                if (stat.isDirectory()) {
                    removeFolder(file, cb);
                } else {
                    fs.unlink(file, function (err) {
                        if (err) {
                            return cb(err);
                        }
                        return cb();
                    })
                }
            })
        }, function (err) {
            if (err) return next(err)
            fs.rmdir(location, function (err) {
                return next(err)
            })
        })
    })
}

4
L'idea è in realtà di non scrivere il proprio codice se è già stato scritto da qualcun altro. Il modo migliore per farlo è usare rimraf o fs-extra o qualsiasi altro modulo nodo, per fare il lavoro per te.
Victor Pudeyev,

90
Sì, scrivere il tuo codice è terribile, perché l'uso di dozzine di moduli di terze parti per operazioni relativamente banali non ha mai dimostrato di avere degli svantaggi in applicazioni su larga scala.
Eric,

8

Se stai usando il nodo 8+ vuoi l'asincronicità e non vuoi dipendenze esterne, ecco la versione asincrona / wait:

const path = require('path');
const fs = require('fs');
const util = require('util');

const readdir = util.promisify(fs.readdir);
const lstat = util.promisify(fs.lstat);
const unlink = util.promisify(fs.unlink);
const rmdir = util.promisify(fs.rmdir);

const removeDir = async (dir) => {
    try {
        const files = await readdir(dir);
        await Promise.all(files.map(async (file) => {
            try {
                const p = path.join(dir, file);
                const stat = await lstat(p);
                if (stat.isDirectory()) {
                    await removeDir(p);
                } else {
                    await unlink(p);
                    console.log(`Removed file ${p}`);
                }
            } catch (err) {
                console.error(err);
            }
        }))
        await rmdir(dir);
        console.log(`Removed dir ${dir}`);
    } catch (err) {
      console.error(err);
    }
}

4

Versione asincrona della risposta di @ SharpCoder usando fs.promises:

const fs = require('fs');
const afs = fs.promises;

const deleteFolderRecursive = async path =>  {
    if (fs.existsSync(path)) {
        for (let entry of await afs.readdir(path)) {
            const curPath = path + "/" + entry;
            if ((await afs.lstat(curPath)).isDirectory())
                await deleteFolderRecursive(curPath);
            else await afs.unlink(curPath);
        }
        await afs.rmdir(path);
    }
};

3

Ho raggiunto qui mentre cercavo di andare oltre gulpe scrivo per ulteriori approfondimenti.

Quando si desidera eliminare file e cartelle utilizzando del, è necessario aggiungere /**per l'eliminazione ricorsiva.

gulp.task('clean', function () {
    return del(['some/path/to/delete/**']);
});

2

Il pacchetto di fatto è rimraf, ma ecco la mia piccola versione asincrona:

const fs = require('fs')
const path = require('path')
const Q = require('q')

function rmdir (dir) {
  return Q.nfcall(fs.access, dir, fs.constants.W_OK)
    .then(() => {
      return Q.nfcall(fs.readdir, dir)
        .then(files => files.reduce((pre, f) => pre.then(() => {
          var sub = path.join(dir, f)
          return Q.nfcall(fs.lstat, sub).then(stat => {
            if (stat.isDirectory()) return rmdir(sub)
            return Q.nfcall(fs.unlink, sub)
          })
        }), Q()))
    })
    .then(() => Q.nfcall(fs.rmdir, dir))
}


2

Secondo la fsdocumentazione , fsPromisesattualmente fornisce l' recursiveopzione su base sperimentale, che, almeno nel mio caso su Windows, rimuove la directory e tutti i file in essa contenuti.

fsPromises.rmdir(path, {
  recursive: true
})

Rimuove recursive: truei file su Linux e MacOS?


1

Ultra-velocità e a prova di guasto

È possibile utilizzare il lignatorpacchetto ( https://www.npmjs.com/package/lignator ), è più veloce di qualsiasi codice asincrono (ad esempio rimraf) e più a prova di errore (specialmente in Windows, dove la rimozione dei file non è istantanea e i file potrebbero essere bloccato da altri processi).

4,36 GB di dati, 28 042 file, 4 217 cartelle su Windows rimosse in 15 secondi contro i 60 secondi di rimraf sul vecchio HDD.

const lignator = require('lignator');

lignator.remove('./build/');

1

Sincronizza cartella rimossa con i file o solo un file.

Non sono un grande donatore né un collaboratore, ma non sono riuscito a trovare una buona soluzione a questo problema e ho dovuto trovare la mia strada ... quindi spero che ti piaccia :)

Funziona perfettamente per me con qualsiasi numero di directory e sottodirectory nidificate. Attenzione per l'ambito di "questo" quando si ricorre alla funzione, l'implementazione potrebbe essere diversa. Nel mio caso questa funzione rimane nel ritorno di un'altra funzione, ecco perché la chiamo così.

    const fs = require('fs');

    deleteFileOrDir(path, pathTemp = false){
            if (fs.existsSync(path)) {
                if (fs.lstatSync(path).isDirectory()) {
                    var files = fs.readdirSync(path);
                    if (!files.length) return fs.rmdirSync(path);
                    for (var file in files) {
                        var currentPath = path + "/" + files[file];
                        if (!fs.existsSync(currentPath)) continue;
                        if (fs.lstatSync(currentPath).isFile()) {
                            fs.unlinkSync(currentPath);
                            continue;
                        }
                        if (fs.lstatSync(currentPath).isDirectory() && !fs.readdirSync(currentPath).length) {
                            fs.rmdirSync(currentPath);
                        } else {
                            this.deleteFileOrDir(currentPath, path);
                        }
                    }
                    this.deleteFileOrDir(path);
                } else {
                    fs.unlinkSync(path);
                }
            }
            if (pathTemp) this.deleteFileOrDir(pathTemp);
        }

1

Mentre recursiveè un'opzione sperimentale difs.rmdir

function rm (path, cb) {
    fs.stat(path, function (err, stats) {
        if (err)
            return cb(err);

        if (stats.isFile())
            return fs.unlink(path, cb);

        fs.rmdir(path, function (err) {
            if (!err || err && err.code != 'ENOTEMPTY') 
                return cb(err);

            fs.readdir(path, function (err, files) {
                if (err)
                    return cb(err);

                let next = i => i == files.length ? 
                    rm(path, cb) : 
                    rm(path + '/' + files[i], err => err ? cb(err) : next(i + 1));

                next(0);
            });
        });
    });
}

1

Aggiornamento 2020

Dalla versione 12.10.0 recursiveOption è stato aggiunto per le opzioni.

Si noti che la cancellazione ricorsiva è sperimentale .

Quindi faresti per la sincronizzazione:

fs.rmdirSync(dir, {recursive: true});

o per asincrono:

fs.rmdir(dir, {recursive: true});

0

Basta usare il modulo rmdir ! è facile e semplice.


6
Non è sempre una buona idea usare un modulo per ogni piccola parte di codice. Se devi creare un contratto di licenza, ad esempio, questo genera un vero dolore.
Mijago,

4
devi aggiungere un esempio di codice per rendere la tua risposta più interessante
Xeltor,

0

Un'altra alternativa consiste nell'utilizzare il fs-promisemodulo che fornisce versioni promesse dei fs-extramoduli

potresti quindi scrivere come in questo esempio:

const { remove, mkdirp, writeFile, readFile } = require('fs-promise')
const { join, dirname } = require('path')

async function createAndRemove() {
  const content = 'Hello World!'
  const root = join(__dirname, 'foo')
  const file = join(root, 'bar', 'baz', 'hello.txt')

  await mkdirp(dirname(file))
  await writeFile(file, content)
  console.log(await readFile(file, 'utf-8'))
  await remove(join(__dirname, 'foo'))
}

createAndRemove().catch(console.error)

nota: async / waitit richiede una versione recente di nodejs (7.6+)


0

Un modo rapido e sporco (forse per il test) potrebbe essere quello di utilizzare direttamente il metodo execo spawnper invocare la chiamata del sistema operativo per rimuovere la directory. Maggiori informazioni su NodeJs child_process .

let exec = require('child_process').exec
exec('rm -Rf /tmp/*.zip', callback)

Gli aspetti negativi sono:

  1. Dipende dal sistema operativo sottostante, ovvero lo stesso metodo verrebbe eseguito in unix / linux ma probabilmente non in Windows.
  2. Non è possibile dirottare il processo su condizioni o errori. Basta assegnare l'attività al sistema operativo sottostante e attendere la restituzione del codice di uscita.

Benefici:

  1. Questi processi possono essere eseguiti in modo asincrono.
  2. È possibile ascoltare l'output / errore del comando, quindi l'output del comando non viene perso. Se l'operazione non viene completata, è possibile controllare il codice di errore e riprovare.

2
Perfetto per quando si scrive script e non si desidera installare dipendenze perché si sta per eliminare questo script in 30 secondi dopo aver eliminato tutti i file !!
Mathias,

Esistono sempre modi per rovinare ed eliminare il filesystem di root. In questo caso OP può rimuovere il -fflag per sicurezza, o assicurarsi durante la digitazione che non cancellerà tutto. exec + rmè un comando valido e utile nel nodo che utilizzo spesso durante i test.
Rash

0

Vorrei che ci fosse un modo per farlo senza moduli aggiuntivi per qualcosa di così minuscolo e comune, ma questo è il migliore che ho potuto inventare.

Aggiornamento: ora dovrebbe funzionare su Windows (testato Windows 10) e dovrebbe funzionare anche su sistemi Linux / Unix / BSD / Mac.

const
    execSync = require("child_process").execSync,
    fs = require("fs"),
    os = require("os");

let removeDirCmd, theDir;

removeDirCmd = os.platform() === 'win32' ? "rmdir /s /q " : "rm -rf ";

theDir = __dirname + "/../web-ui/css/";

// WARNING: Do not specify a single file as the windows rmdir command will error.
if (fs.existsSync(theDir)) {
    console.log(' removing the ' + theDir + ' directory.');
    execSync(removeDirCmd + '"' + theDir + '"', function (err) {
        console.log(err);
    });
}

Forse fs-extra è la strada da percorrere se vuoi un singolo modulo.
b01,

3
Questo metodo è decisamente pericoloso. La concatenazione di stringhe di un comando shell, soprattutto senza escape, invita a vulnerabilità nell'esecuzione di codice e simili. Se hai intenzione di usare rmdir, usa child_process.execFileciò che non invoca la shell e passa invece argomenti in modo esplicito.
nevyn,

@nevyn Ci proverò e aggiornerò la mia risposta se funziona.
b01,

Preferisci sempre non usare terze parti! Grazie!
Anton Mitsev,

Inoltre, questo metodo è piuttosto lento. L'API nativa di Nodejs è molto più veloce.
Mersey,

0

Questo è un approccio che utilizza promisify e due funzioni di aiuto (da e verso All) per risolvere la promessa.

Fa tutte le azioni asincrone.

const fs = require('fs');
const { promisify } = require('util');
const to = require('./to');
const toAll = require('./toAll');

const readDirAsync = promisify(fs.readdir);
const rmDirAsync = promisify(fs.rmdir);
const unlinkAsync = promisify(fs.unlink);

/**
    * @author Aécio Levy
    * @function removeDirWithFiles
    * @usage: remove dir with files
    * @param {String} path
    */
const removeDirWithFiles = async path => {
    try {
        const file = readDirAsync(path);
        const [error, files] = await to(file);
        if (error) {
            throw new Error(error)
        }
        const arrayUnlink = files.map((fileName) => {
            return unlinkAsync(`${path}/${fileName}`);
        });
        const [errorUnlink, filesUnlink] = await toAll(arrayUnlink);
        if (errorUnlink) {
            throw new Error(errorUnlink);
        }
        const deleteDir = rmDirAsync(path);
        const [errorDelete, result] = await to(deleteDir);
        if (errorDelete) {
            throw new Error(errorDelete);
        }
    } catch (err) {
        console.log(err)
    }
}; 

0

// senza utilizzo di lib di terze parti

const fs = require('fs');
var FOLDER_PATH = "./dirname";
var files = fs.readdirSync(FOLDER_PATH);
files.forEach(element => {
    fs.unlinkSync(FOLDER_PATH + "/" + element);
});
fs.rmdirSync(FOLDER_PATH);

1
fs.unllinkSync(path.join(FOLDER_PATH, element);
Funzionerà

-1
const fs = require("fs")
const path = require("path")

let _dirloc = '<path_do_the_directory>'

if (fs.existsSync(_dirloc)) {
  fs.readdir(path, (err, files) => {
    if (!err) {
      for (let file of files) {
        // Delete each file
        fs.unlinkSync(path.join(_dirloc, file))
      }
    }
  })
  // After the 'done' of each file delete,
  // Delete the directory itself.
  if (fs.unlinkSync(_dirloc)) {
    console.log('Directory has been deleted!')
  }
}

1
Penso che qualcosa del genere dovrebbe funzionare per le directory nidificate.
fool4jesus,

Sì, sia per la directory nidificata che per quella non nidificata
Erisan Olasheni,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.