Exemple #1
0
        /// <summary>
        /// Metoda Close, jen pro lepší přehlednost - prostě zavolá Dispose
        /// </summary>
        public void Close()
        {
            FilesIn.Clear();
            FilesToAdd.Clear();
            FilesToRemove.Clear();

            Dispose();
        }
Exemple #2
0
        /// <summary>
        /// Odstraní soubor - rozliší jestli je odstraňovaný soubor už fyzicky v archívu nebo má být teprve přidán po uložení
        /// </summary>
        /// <param name="Name">Název souboru k odstranění</param>
        public void RemoveFile(string Name)
        {
            // Zkus soubor prvně najít a odstranit ze seznamu nových souborů k přidání do archívu
            for (int i = 0; i < FilesToAdd.Count; i++)
            {
                // V kolekci FilesToAdd jsou absolutní cesty k souborů, my chceme však jen jejich názvy s příponou k porovnání
                if (Path.GetFileName(FilesToAdd[i].ToString()) == Name)
                {
                    FilesToAdd.RemoveAt(i); // Odstraň a skonči metodu
                    return;
                }
            }

            // Když tam nebyl, zkus najít a označit ke smazání takový soubor v seznamu souborů v archívu - když tam nebude metoda skončí vyjímkou (protože pomocní metoda FindFileByName skončí vyjímkou)
            FilesToRemove.Add(FindFileByName(Name));
        }
Exemple #3
0
        /// <summary>
        /// Přidá nějaký existující soubor do seznamu k přidání do archívu
        /// </summary>
        /// <param name="PathToFile">Cesta k souboru</param>
        public void AddFile(string PathToFile)
        {
            if (!File.Exists(PathToFile))
            {
                throw new ArchieverException("Soubor neexistuje!");
            }

            // Nedovol duplicitu názvů souborů
            if (FilesIn.IndexOf(Path.GetFileName(PathToFile)) >= 0 || FilesToAdd.IndexOf(Path.GetFileName(PathToFile)) >= 0)
            {
                throw new ArchieverException("Soubor s takovým jménem už v archívu nebo v seznamu souborů pro přidání existuje!");
            }

            // Tady neděláme nic jiného že přidáváme do kolekce FilesToAdd. Samotné fyzické přidání proběhne až při zavolání SaveArchieve.
            FilesToAdd.Add(PathToFile);
        }
Exemple #4
0
        public Task CommitAsync()
        {
            try
            {
                foreach (var file in FilesToAdd)
                {
                    SaveFile(file);
                }

                foreach (var file in FilesToRemove)
                {
                    RemoveFile(file);
                }

                FilesToAdd.Clear();
                FilesToRemove.Clear();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }

            return(Task.CompletedTask);
        }
Exemple #5
0
        /// <summary>
        /// Nejsložitější metoda v celé třídě. Má na starost uložení změn provedených v archívu.
        /// </summary>
        public void SaveArchieve()
        {
            // Metoda nemá smysl, pokud není otevřen nějaký soubor
            if (!IsOpened)
            {
                throw new ArchieverException("Nepracujete s žádným souborem!");
            }

            // Do tohoto MemoryStreamu načteme všechny staré věci které mají v archívu zůstat (nebyly odstraněny) a zároveň do něj přidáme nové věci.
            using (MemoryStream MyMemory = new MemoryStream())
            {
                // Musíme tedy projít všechny soubory které chceme zachovat a "přeuložit" je do streamu MyMemory
                int ToJump = 0, CurrentSize = 0;
                for (int i = 0; i < FilesIn.Count; i++)
                {
                    // CurrentSize používám jen jako pomocnou proměnnou abych pořád nemusel dělat toto nepěkné přetypování
                    CurrentSize = ((FileHeader)FilesIn[i]).Size;

                    // Jesliže je momentální index v poli indexů pro odstranění z archívu, vynecháme jej, ale musíme s ním počítat i při přeskoku pomocí Seek
                    if (FilesToRemove.IndexOf(i) > -1)
                    {
                        ToJump += CurrentSize;
                        continue; // Jak jistě víte, příkaz continue ukončí momentální iteraci, ale ne samotný cyklus.
                    }

                    // Přeskoč na daný soubor
                    MyArchieve.Seek((long)(FilesBegin + ToJump), SeekOrigin.Begin);

                    // Opět se zde vědomě dopouštím stejné chyby jako v indexeru, viz. komentáře v kódu indexeru
                    byte[] buffer = new byte[CurrentSize];
                    MyArchieve.Read(buffer, 0, CurrentSize);
                    MyMemory.Write(buffer, 0, CurrentSize);

                    ToJump += CurrentSize;
                }

                // Teď už můžeme z FilesIn skutečně bezpečně odstranit všechny ty indexy, které jsou v poli FilesToRemove
                foreach (int index in FilesToRemove)
                {
                    FilesIn.RemoveAt(index);
                }

                // Další krok bude přidání nových souborů do archívu...

                byte[] buf = new byte[1024];
                // V BytesRead bude počet bajtů který se přečtl při jedné blokové operaci.V TotalSize nasčítáme počet všech přečtěných bajtů, což je totéž jako velikost souboru
                int BytesRead = 0, TotalSize = 0;
                foreach (string PathToFile in FilesToAdd)
                {
                    try // Zde by mohla nastat spousta špatností, protože pracujeme s filesystémem
                    {
                        // Toto je již velmi známá konstrukce čtení dat po pevných blocích - netřeba dál komentovat.
                        using (FileStream MyNewFile = File.OpenRead(PathToFile))
                        {
                            TotalSize = 0;
                            while ((BytesRead = MyNewFile.Read(buf, 0, 1024)) > 0)
                            {
                                MyMemory.Write(buf, 0, BytesRead);
                                TotalSize += BytesRead;
                            }
                        }
                    }
                    catch (Exception e) // Chytej jakoukoliv vyjímku
                    {
                        // Vyhoď vyjímku ArchieverException s textem původní vyjímky která nastala
                        throw new ArchieverException(e.Message);
                    } // Všimněte si, že díky bloku using() nemusíme používat finally a v něm stream zavírat!

                    // Když všechno prošlo bez chyby, tak vytvoříme pro nový soubor hlavičku a přidáme ji do pole FilesIn
                    FileHeader MyNewHeader = new FileHeader();
                    MyNewHeader.Name = Path.GetFileName(PathToFile);
                    MyNewHeader.Size = TotalSize;
                    FilesIn.Add(MyNewHeader);
                }

                // Vyčistíme kolekce FilesToAdd a FilesToRemove
                FilesToAdd.Clear();
                FilesToRemove.Clear();

                // V posledním kroku prostě přepíšeme původní stream MyArchieve novou hlavičkou a tím co máme v MyMemory streamu
                MyArchieve.Seek(0, SeekOrigin.Begin);
                MyArchieve.SetLength(0); // Toto zničí všechny data ve streamu (zkrátí délku souboru na 0)

                // První čtyři bajty archívu je počet souborů v něm (opět používáme BitConverter)
                MyArchieve.Write(BitConverter.GetBytes(FilesIn.Count), 0, 4);

                // Teď zapíšeme názvy souborů (s ohledem na 150 bajtový limit jeho délky) a hned za něj jeho velikost, celkem tedy 154 bajtů (v případě že někdo nezmění NameMaxLength na jinou hodnotu než 150)
                foreach (FileHeader item in FilesIn)
                {
                    // Toto pole bajtů má přesně takovou velikost jako je délka názvu souboru v kódování systému
                    byte[] bytesOfName = Encoding.Default.GetBytes(item.Name);

                    // Musíme velikost pole bytesOfName zarovnat na NameMaxLength, což je v našem případě 150
                    byte[] normalizedName = new byte[NameMaxLength];

                    // Pozorně prosím prostudujte jak pracuje tento cyklus (kopíruje z pole s názvem souboru do normalizovaného pole od délce NameMaxLength, když je delší - oseká se)
                    for (int i = 0; i < (bytesOfName.Length > NameMaxLength ? NameMaxLength : bytesOfName.Length); i++)
                    {
                        normalizedName[i] = bytesOfName[i];
                    }

                    MyArchieve.Write(normalizedName, 0, NameMaxLength);
                    MyArchieve.Write(BitConverter.GetBytes(item.Size), 0, 4);
                }

                // ... a úplně nakonec zapíšeme všechny soubory, které již máme připraveny v MyMemory - tam jsou ve stejném pořadí tak jako v hlavičce.
                MyArchieve.Write(MyMemory.ToArray(), 0, (int)MyMemory.Length);
            } // A HOTOVO! (Všimněte si, že celý kód metody byl v postatě "zabalen" v bloku od using(MemoryStream MyMemory ...) takže ať se stalo cokoliv bude vždycky zavřen!
        }
Exemple #6
0
 public void Add(File file)
 {
     FilesToAdd.Add(file);
 }