PDA

View Full Version : [C#] Sincronizzare il contenuto di una cartella con quello di un archivio ZIP


alex783
02-11-2009, 08:21
Ciao a tutti!
Ieri sera ho scritto una classe per realizzare il backup incrementale di una cartella in formato ZIP (utilizzando l'ottimo DotNetZip di Dino Chiesa ( :ave: )), solo che sono rimasto abbastanza deluso dalle prestazioni di ciò che ho fatto.

In particolare, usando la cartella del profilo di Thunderbird come test (la mia "pesa" circa 650MB e contiene circa 6500 files e poco più di 80 cartelle totali), il primo backup completo (del peso finale di 340MB) è realizzato in 3 minuti e 23 secondi (e questo dimostra la bontà della libreria DotNetZip, con prestazioni paragonabili ai più noti ZIP engine), mentre i backup successivi, quindi del tipo incrementale, sono completati in circa 2 minuti e 35 secondi per un peso di circa 160MB ciascuno (questo perché basta ricevere solo un'email e Thuderbird modifica il file Inbox del relativo account e quindi va segnato per il backup (in genere è un file piuttosto grande).

Bene. Mi sono accorto che durante i backup incrementali, la mia classe perde ben 1 minuto e 17 secondi per determinare (attraverso un ciclo foreach) quali sono i file da includere nel nuovo file ZIP. Ed è qui che, secondo me, perde davvero troppo tempo.

Ecco il mio codice, che poi non è nulla di che, ovviamente.


using (ZipFile NewZip = new ZipFile())
{
ZipFile OldZip = ZipFile.Read(nameOfZipFile)

NewZip.UseZip64WhenSaving = Zip64Option.AsNecessary;
NewZip.CompressionLevel = CompressionLevel.Level2;
NewZip.UseUnicodeAsNecessary = true;

foreach (FileInfo currentFileOnDisk in FileList)
{
ZipEntry candidateZipEntry = OldZip[currentFileOnDisk.FullName.Replace(mailclientsClass.thunderbirdProfilePath, "ThunderbirdProfile")];
if (candidateZipEntry != null && currentFileOnDisk.LastWriteTimeUtc > candidateZipEntry.ModifiedTime)
NewZip.AddFile(currentFileOnDisk.FullName, Path.GetDirectoryName(currentFileOnDisk.FullName.Replace(mailclientsClass.thunderbirdProfilePath, "ThunderbirdProfile")));
else if (candidateZipEntry == null)
NewZip.AddFile(currentFileOnDisk.FullName, Path.GetDirectoryName(currentFileOnDisk.FullName.Replace(mailclientsClass.thunderbirdProfilePath, "ThunderbirdProfile")));
}

NewZip.Save(nameOfZipFile+incrementalCounter.ToString());
}

Secondo voi sbaglio qualcosa? come si potrebbe fare di meglio? :stordita:

P.S: la classe non è ovviamente completa, manca in particolare il controllo di eventuali file eliminati nella cartella origine, che dovrei realizzare con un altro ciclo foreach, e quindi immagino che perderà un altro minuto per il controllo :muro:

alex783
03-11-2009, 07:41
ehm... nessuno? :stordita:

banryu79
03-11-2009, 10:48
Potrei dire una cavolata, ma potresti eseguire l'operazione qui sotto solo una volta, invece che ripeterla 2 volte:
- durante l'assegnazione iniziale di ZipEntry;
- nel ramo di esecuzione scelto.

currentFileOnDisk.FullName.Replace(mailclientsClass.thunderbirdProfilePath, "ThunderbirdProfile")


poi potresti scrivere la condizione dell'if così:

foreach (FileInfo currentFileOnDisk in FileList)
{
String fileName = currentFileOnDisk.FullName.Replace(mailclientsClass.thunderbirdProfilePath, "ThunderbirdProfile");
ZipEntry candidateZipEntry = OldZip[fileName];

if (candidateZipEntry == null || currentFileOnDisk.LastWriteTimeUtc > candidateZipEntry.ModifiedTime)
NewZip.AddFile(currentFileOnDisk.FullName, Path.GetDirectoryName(fileName));
}

theking0
03-11-2009, 14:47
La migliore lib per il formatto zip è SharpZipLib (http://www.icsharpcode.net/OpenSource/SharpZipLib/).

Prova con quella.

alex783
03-11-2009, 17:07
Potrei dire una cavolata, ma potresti eseguire l'operazione qui sotto solo una volta, invece che ripeterla 2 volte:
- durante l'assegnazione iniziale di ZipEntry;
- nel ramo di esecuzione scelto.

currentFileOnDisk.FullName.Replace(mailclientsClass.thunderbirdProfilePath, "ThunderbirdProfile")


poi potresti scrivere la condizione dell'if così:

foreach (FileInfo currentFileOnDisk in FileList)
{
String fileName = currentFileOnDisk.FullName.Replace(mailclientsClass.thunderbirdProfilePath, "ThunderbirdProfile");
ZipEntry candidateZipEntry = OldZip[fileName];

if (candidateZipEntry == null || currentFileOnDisk.LastWriteTimeUtc > candidateZipEntry.ModifiedTime)
NewZip.AddFile(currentFileOnDisk.FullName, Path.GetDirectoryName(fileName));
}



Sì, in effetti la condizione di "if" è meglio come hai detto tu.
Thanks! ;)

La migliore lib per il formatto zip è SharpZipLib (http://www.icsharpcode.net/OpenSource/SharpZipLib/).

Prova con quella.

Uhm, perché è meglio? DotNetZip è un progetto in continuo sviluppo, il manteiner rilascia nuove versione molto frequentemente e la documentazione è ottima.

Inoltre, la versione 1.9 promette grandi passi avanti nelle prestazioni (che già sono assai decorose, comunque) con la compressione e decompressione in parallelo ( http://dotnetzip.codeplex.com/WorkItem/View.aspx?WorkItemId=5183 )

alex783
05-11-2009, 09:46
Dino Chiesa ha rilasciato una nuova release della versione beta di DotNetZip (la 1.9.0.29).

Dire "eccezionale" sarebbe riduttivo :sofico:

Con la compressione in parallelo e il BufferSize impostato a 131072 (ovvero 128KB), il mio programma ha ridotto i tempi di compressione di circa l'80%. :sofico:

Per comprimere la stessa cartella del profilo di Thunderbird, ora impiega appena 1 minuto e 52 secondi (con la vecchia versione della libreria, e senza il BufferSize impostato a 128KB, impiegava circa 3 minuti e 23 secondi). :D :D :D

E devo ancora provare gli altri consigli che mi ha dato! :cool: :D

Provatela anche voi! http://dotnetzip.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=27890

alex783
05-11-2009, 09:49
Inoltre, stavo pensando anche di impiegare un db come SQLite per creare un catalogo che tenga traccia degli ultimi file modificati, così non ho bisogno di controllare ogni file ZIP. Non vedo l'ora di provare tutte queste modifiche... :D