Rozpakuj pliki programowo w .net

221

Próbuję programowo rozpakować skompresowany plik.

Próbowałem użyć System.IO.Compression.GZipStreamklasy w .NET, ale kiedy moja aplikacja działa (właściwie test jednostkowy), otrzymuję ten wyjątek:

System.IO.InvalidDataException: Magiczna liczba w nagłówku GZip jest niepoprawna. Upewnij się, że przekazujesz strumień GZip.

Teraz zdaję sobie sprawę, że .zipplik nie jest taki sam jak .gzplik i GZipto nie to samo co Zip.

Ponieważ jednak mogę wyodrębnić plik, ręcznie klikając dwukrotnie plik spakowany, a następnie klikając przycisk „Wyodrębnij wszystkie pliki”, myślę, że powinien istnieć sposób na zrobienie tego również w kodzie.

Dlatego próbowałem użyć Process.Start()ścieżki do skompresowanego pliku jako danych wejściowych. Powoduje to, że moja aplikacja otwiera okno pokazujące zawartość w spakowanym pliku. Wszystko w porządku, ale aplikacja zostanie zainstalowana na serwerze i nie będzie nikogo, kto kliknie przycisk „Wyodrębnij wszystkie pliki”.

Jak mogę uzyskać aplikację, aby wyodrębniała pliki z plików spakowanych?

Czy jest inny sposób? Wolę robić to w kodzie, bez pobierania bibliotek lub aplikacji stron trzecich; Departament Bezpieczeństwa nie jest zbyt fantazyjny ...

Petteri
źródło
12
Twój dział bezpieczeństwa jest szczęśliwszy, gdy piszesz własny kod do czegoś, niż przy użyciu biblioteki, która została debugowana i oglądana przypuszczalnie wieloma oczami? Możesz użyć biblioteki ORAZ „zrób to w kodzie” (pobierz źródło i skompiluj ją samodzielnie), ale widzę, że wynalezienie koła jest większym problemem niż jakiekolwiek problemy związane z bezpieczeństwem spowodowane użyciem sprawdzonej i prawdziwej biblioteki.
Jared Updike,
10
@Jared - Kiedy zarząd wpadł na pomysł ...
Steven Evers
4
Jeśli otrzymasz produkt innej firmy, ryzyko dla działu bezpieczeństwa jest mniejsze. Wystarczy pobrać dotnetzip i zmienić jego nazwę na „[wstaw nazwę firmy] .ziplibrary.dll”
Simon

Odpowiedzi:

59

Z powodzeniem korzystaliśmy z SharpZipLib w wielu projektach. Wiem, że jest to narzędzie innej firmy, ale kod źródłowy jest dołączony i może zapewnić pewien wgląd, jeśli zdecydujesz się na nowo wynaleźć koło tutaj.

Chris Conway
źródło
3
Próbowałem użyć SharpZipLib i działało dobrze. Chyba będę musiał sprawdzić, czy zakaz korzystania z bibliotek stron trzecich i apss jest surową zasadą, czy może bardziej wytyczną.
Petteri
10
Nie wiem o Twojej firmie, ale z mojego doświadczenia zawsze wynikało, że można uzyskać wyjątek od tego rodzaju reguły, jeśli napiszesz opis uzasadnienia biznesowego, dlaczego chcesz wyjątek. Wskaż oszczędność kosztów w porównaniu z majsterkowaniem, a także fakt, że można zbadać źródło. Jako rezerwowy często możesz uzyskać pozwolenie na korzystanie ze źródła, nawet jeśli nie pozwala ono na użycie biblioteki dll - po prostu skompiluj ją samodzielnie (lub przynajmniej części, których faktycznie potrzebujesz użyć ...).
RolandTumble,
Nie musisz używać zewnętrznych bibliotek do rozpakowywania plików zip, możesz użyć Shell32 z System32. Zobacz stackoverflow.com/a/43066281/948694
art.
490

W .NET 4.5 możesz teraz rozpakować pliki za pomocą .NET Framework:

using System;
using System.IO;

namespace ConsoleApplication
{
  class Program
  {
    static void Main(string[] args)
    {
      string startPath = @"c:\example\start";
      string zipPath = @"c:\example\result.zip";
      string extractPath = @"c:\example\extract";

      System.IO.Compression.ZipFile.CreateFromDirectory(startPath, zipPath);
      System.IO.Compression.ZipFile.ExtractToDirectory(zipPath, extractPath);
    }
  }
}

Powyższy kod pochodzi bezpośrednio z dokumentacji Microsoft: http://msdn.microsoft.com/en-us/library/ms404280(v=vs.110).aspx

ZipFilejest zawarty w zestawie System.IO.Compression.FileSystem. (Dzięki nateirvin ... patrz komentarz poniżej)

bsara
źródło
118
BTW, ZipFilejest zawarty w zestawie System.IO.Compression.FileSystem.
nateirvin
73
Co oznacza, że ​​musisz dodać odwołanie DLL do zestawu frameworka System.IO.Compression.FileSystem.dll.
Chris Schiffhauer,
co z plikami .rar. powyższy kod nie może wyodrębnić plików .rar.
Raghu
1
Próbowałem tego w interfejsie API asp.net core, pierwszy wpis przeczytał dobrze, ale przy drugim wpisie zawsze daje błąd A local file header is corrupt. Ktoś na to?
SoftSan
To samo z @SoftSan. Mam też ten błąd. Co robić?
Bogaty
101

Dla .Net 4.5+

Zapisywanie nieskompresowanego pliku na dysku nie zawsze jest pożądane. Jako programista ASP.Net musiałbym majstrować przy uprawnieniach do nadawania uprawnień mojej aplikacji do zapisu w systemie plików. Pracując ze strumieniami w pamięci, mogę ominąć to wszystko i bezpośrednio odczytać pliki:

using (ZipArchive archive = new ZipArchive(postedZipStream))
{
    foreach (ZipArchiveEntry entry in archive.Entries)
    {
         var stream = entry.Open();
         //Do awesome stream stuff!!
    }
}

Alternatywnie nadal możesz zapisać rozpakowany plik na dysk, wywołując ExtractToFile():

using (ZipArchive archive = ZipFile.OpenRead(pathToZip))
{
    foreach (ZipArchiveEntry entry in archive.Entries)
    {
        entry.ExtractToFile(Path.Combine(destination, entry.FullName));
    }
} 

Aby użyć tej ZipArchiveklasy, musisz dodać odwołanie do System.IO.Compressionprzestrzeni nazw i do System.IO.Compression.FileSystem.

Mister Epic
źródło
8
Czy naprawdę trzeba było MSFT do wersji 4.5+, aby dodać natywny dekompresor?
John Peters
2
@JohnPeters GZipStream został ponownie dodany w .Net 2.0 ( msdn.microsoft.com/en-us/library/… ). Jednak nie ułatwiło to pracy z wieloma plikami w archiwum w pamięci. Nowy ZipArchiveobiekt ładnie pasuje do rachunku.
Mister Epic
1
Jest to szczególnie dobra alternatywa, ponieważ umożliwia rozpakowanie bez użycia systemu plików (w moim przypadku pracuję z zasobami osadzonymi), a także nie jest rozszerzeniem innej firmy.
ANeves
1
Dlaczego powinienem używać foreachpętli, ExtractToFilekiedy mogę po prostu użyć ZipFile.ExtractToDirectory(inputFile, outputDir);Jakie są zalety pierwszej metody?
Fluffy Robot
1
w .NET 4.6.1 nie mogę uzyskać „ZipArchive” z „System.IO.Compression.FileSystem”, jakiś pomysł?
Ravi Anand
55

Darmowe i brak zewnętrznych plików DLL. Wszystko jest w jednym pliku CS. Jedno pobranie to tylko plik CS, inne pobranie jest bardzo łatwym do zrozumienia przykładem. Właśnie wypróbowałem go dzisiaj i nie mogę uwierzyć, jak prosta była konfiguracja. Działa przy pierwszej próbie, bez błędów, bez niczego.

https://github.com/jaime-olivares/zipstorer

Lukas
źródło
Mówiłem za wcześnie! Chcę natychmiast nadmuchać pliki ze strumienia pobierania HTTP. To nie działa, ponieważ używa operacji Seek w strumieniu :( Cóż, dzięki kodowi źródłowemu mogę teraz napisać własny ZipStream ...
oyophant
najlepsze rozwiązanie mojego problemu, ponieważ piszę aplikację aktualizującą i nie mogę zaangażować żadnych bibliotek DLL w proces wyodrębniania, od tego czasu musiałbym je również zaktualizować .... to dobrze. dziękuję!
Niklas
27

Skorzystaj z biblioteki DotNetZip pod adresem http://www.codeplex.com/DotNetZip

biblioteka klas i zestaw narzędzi do manipulowania plikami zip. Użyj VB, C # lub dowolnego języka .NET, aby łatwo tworzyć, rozpakowywać lub aktualizować pliki zip ...

DotNetZip działa na komputerach z pełną platformą .NET Framework, a także działa na urządzeniach mobilnych korzystających z .NET Compact Framework. Twórz i odczytuj pliki zip w VB, C #, dowolnym języku .NET lub w dowolnym środowisku skryptowym ...

Jeśli wszystko, czego potrzebujesz, to lepsza klasa DeflateStream lub GZipStream, która zastąpi klasę wbudowaną w .NET BCL, DotNetZip również to ma. DeflateStream i GZipStream firmy DotNetZip są dostępne w osobnym zestawie, opartym na porcie .NET Zlib. Strumienie te obsługują poziomy kompresji i zapewniają znacznie lepszą wydajność niż klasy wbudowane. Istnieje również ZlibStream do uzupełnienia zestawu (RFC 1950, 1951, 1952) ...

Sam Axe
źródło
1
Hmmm ... Ale to biblioteka innej firmy!
Petteri
30
Jak bardzo spostrzegawczy. Jeśli nie masz ochoty spędzić kilku miesięcy na wdrażaniu własnego czytnika plików Zip, jest to najlepsza opcja.
Sam Axe
Ten jest znacznie lepszy niż SharpZipLib
Kugel
5
Zadajesz mi pytania na temat odpowiedzi, która ma prawie 5 lat. Zrobić rozeznanie. Jestem pewien, że znajdziesz odpowiedź.
Sam Axe
2
@PhilCooper To bardzo stare pytanie, które zalecam przy użyciu wbudowanego System.IO.Compression.ZipFile. IIRC Miałem bardzo złe doświadczenia z SharpZipLib w przeszłości, w oparciu o moje doświadczenie w tworzeniu tysięcy zamków błyskawicznych.
Kugel
9
String ZipPath = @"c:\my\data.zip";
String extractPath = @"d:\\myunzips";
ZipFile.ExtractToDirectory(ZipPath, extractPath);

Aby użyć klasy ZipFile, należy dodać odwołanie do zestawu System.IO.Compression.FileSystem w swoim projekcie

Mahadev Mane
źródło
2

Standardowe pliki zip zwykle używają algorytmu deflacji.

Aby wyodrębnić pliki bez korzystania z bibliotek stron trzecich, użyj DeflateStream. Będziesz potrzebował nieco więcej informacji na temat formatu archiwum zip, ponieważ Microsoft zapewnia tylko algorytm kompresji.

Możesz także spróbować użyć zipfldr.dll. Jest to biblioteka kompresji Microsoft (skompresowane foldery z menu Wyślij do). Wygląda na bibliotekę com, ale jest nieudokumentowana. Możesz być w stanie sprawić, by działał dla ciebie poprzez eksperymenty.

Kenneth Cochran
źródło
Próbuję klasy DeflateStream. Tym razem otrzymuję System.IO.InvalidDataException: Długość bloku nie zgadza się z jego dopełnieniem ..
Petteri
Jak powiedziałem powyżej, Microsoft dostarczył tylko algorytm. Potrzebne będą również informacje o formacie archiwum zip. en.wikipedia.org/wiki/ZIP_(file_format) powinien zacząć. Zobacz odnośniki na dole strony, aby uzyskać linki do bardziej szczegółowych informacji.
Kenneth Cochran
2
Natknąłem się również na przeklęty System.IO.Packaging.Package w .NET 3.5. Wygląda na to, że może załatwić sprawę, choć nie jest to zbyt intuicyjne.
Kenneth Cochran
2

Używam tego do spakowania lub rozpakowania wielu plików. Materiały Regex nie są wymagane, ale używam ich do zmiany datownika i usuwania niechcianych podkreśleń. Używam pustego ciągu w ciągu Compress >> zipPath, aby w razie potrzeby poprzedzić coś do wszystkich plików. Ponadto zwykle komentuję Compress () lub Decompress () w oparciu o to, co robię.

using System;
using System.IO.Compression;
using System.IO;
using System.Text.RegularExpressions;

namespace ZipAndUnzip
{
    class Program
    {
        static void Main(string[] args)
        {
            var directoryPath = new DirectoryInfo(@"C:\your_path\");

            Compress(directoryPath);
            Decompress(directoryPath);
        }

        public static void Compress(DirectoryInfo directoryPath)
        {
            foreach (DirectoryInfo directory in directoryPath.GetDirectories())
            {
                var path = directoryPath.FullName;
                var newArchiveName = Regex.Replace(directory.Name, "[0-9]{8}", "20130913");
                newArchiveName = Regex.Replace(newArchiveName, "[_]+", "_");
                string startPath = path + directory.Name;
                string zipPath = path + "" + newArchiveName + ".zip";

                ZipFile.CreateFromDirectory(startPath, zipPath);
            }

        }

        public static void Decompress(DirectoryInfo directoryPath)
        {
            foreach (FileInfo file in directoryPath.GetFiles())
            {
                var path = directoryPath.FullName;
                string zipPath = path + file.Name;
                string extractPath = Regex.Replace(path + file.Name, ".zip", "");

                ZipFile.ExtractToDirectory(zipPath, extractPath);
            }
        }


    }
}
Phyllip Hamby
źródło
Wymaga to dot net 4.5 - tylko notatki, jak zauważyli inni, którzy odpowiedzieli ZipFile, a ja wciąż używam 3.5.
Thronk
2

To wystarczy System.IO.Compression.ZipFile.ExtractToDirectory(ZipName, ExtractToPath)

Ilya Kochetov
źródło
1

Od tutaj :

Skompresowane obiekty GZipStream zapisane do pliku z rozszerzeniem .gz można dekompresować za pomocą wielu popularnych narzędzi do kompresji; jednak ta klasa z natury nie zapewnia funkcji dodawania plików ani wyodrębniania plików z archiwów .zip.

RedWolves
źródło
1

Możesz to wszystko zrobić w .NET 3.5 za pomocą DeflateStream. Brakuje w .NET 3.5 możliwości przetwarzania sekcji nagłówków plików, które służą do organizowania spakowanych plików. PKWare opublikowało te informacje, których można użyć do przetworzenia pliku zip po utworzeniu używanych struktur. Nie jest to szczególnie uciążliwe i jest dobrą praktyką w budowaniu narzędzi bez użycia kodu innej firmy.

To nie jest odpowiedź jednoliniowa, ale jest całkowicie wykonalna, jeśli sam chcesz i nie spiesz się. Napisałem klasę, aby to zrobić w ciągu kilku godzin, a otrzymałem z tego możliwość zipowania i rozpakowywania plików tylko przy użyciu .NET 3.5.

Michael Blake
źródło
0

Dowiedziałem się o tym dzisiaj (pakiet Unzip na NuGet), ponieważ natknąłem się na poważny błąd w DotNetZip i zdałem sobie sprawę, że tak naprawdę niewiele pracy wykonano w DotNetZip przez ostatnie dwa lata.

Pakiet Unzip jest szczupły i wykonał to za mnie - nie miał błędu, który miał DotNetZip. Był to również stosunkowo niewielki plik, opierający się na Microsoft BCL dla faktycznej dekompresji. Z łatwością mogłem dokonać niezbędnych korekt (aby móc śledzić postęp podczas dekompresji). Polecam to.

Per Lundberg
źródło
0

Z Osadzonych zasobów:

using (Stream _pluginZipResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(programName + "." + "filename.zip"))
{
    using (ZipArchive zip = new ZipArchive(_pluginZipResourceStream))
    {
        zip.ExtractToDirectory(Application.StartupPath);
    }
}
Steve Rousseau
źródło
0

Do tej pory korzystałem z procesów cmd, aby wyodrębnić plik .iso, skopiować go do tymczasowej ścieżki z serwera i wyodrębnić na pamięci USB. Ostatnio odkryłem, że działa to doskonale z .iso, które są mniejsze niż 10 Gb. W przypadku ISO, takiego jak 29Gb, ta metoda jakoś utknęła.

    public void ExtractArchive()
    {
        try
        {

            try
            {
                Directory.Delete(copyISOLocation.OutputPath, true); 
            }
            catch (Exception e) when (e is IOException || e is UnauthorizedAccessException)
            {
            }

            Process cmd = new Process();
            cmd.StartInfo.FileName = "cmd.exe";
            cmd.StartInfo.RedirectStandardInput = true;
            cmd.StartInfo.RedirectStandardOutput = true;
            cmd.StartInfo.CreateNoWindow = true;
            cmd.StartInfo.UseShellExecute = false;
            cmd.StartInfo.WindowStyle = ProcessWindowStyle.Normal;

            //stackoverflow
            cmd.StartInfo.Arguments = "-R";

            cmd.Disposed += (sender, args) => {
                Console.WriteLine("CMD Process disposed");
            };
            cmd.Exited += (sender, args) => {
                Console.WriteLine("CMD Process exited");
            };
            cmd.ErrorDataReceived += (sender, args) => {
                Console.WriteLine("CMD Process error data received");
                Console.WriteLine(args.Data);
            };
            cmd.OutputDataReceived += (sender, args) => {
                Console.WriteLine("CMD Process Output data received");
                Console.WriteLine(args.Data);
            };

            //stackoverflow


            cmd.Start();

            cmd.StandardInput.WriteLine("C:");
            //Console.WriteLine(cmd.StandardOutput.Read());
            cmd.StandardInput.Flush();

            cmd.StandardInput.WriteLine("cd C:\\\"Program Files (x86)\"\\7-Zip\\");
            //Console.WriteLine(cmd.StandardOutput.ReadToEnd());
            cmd.StandardInput.Flush();

            cmd.StandardInput.WriteLine(string.Format("7z.exe x -o{0} {1}", copyISOLocation.OutputPath, copyISOLocation.TempIsoPath));
            //Console.WriteLine(cmd.StandardOutput.ReadToEnd());
            cmd.StandardInput.Flush();
            cmd.StandardInput.Close();
            cmd.WaitForExit();
            Console.WriteLine(cmd.StandardOutput.ReadToEnd());
            Console.WriteLine(cmd.StandardError.ReadToEnd());
Răzvan Bălan
źródło
0

możesz użyć kodu wiersza polecenia Info-unzip. wystarczy pobrać unzip.exe z oficjalnej strony Info-unzip.

 internal static void Unzip(string sorcefile)
    {
        try
        {
            AFolderFiles.AFolderFilesDelete.DeleteFolder(TempBackupFolder); // delete old folder   
            AFolderFiles.AFolderFilesCreate.CreateIfNotExist(TempBackupFolder); // delete old folder   
           //need to Command command also to export attributes to a excel file
            System.Diagnostics.Process process = new System.Diagnostics.Process();
            System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
            startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; // window type
            startInfo.FileName = UnzipExe;
            startInfo.Arguments = sorcefile + " -d " + TempBackupFolder;
            process.StartInfo = startInfo;
            process.Start();
            //string result = process.StandardOutput.ReadToEnd();
            process.WaitForExit();
            process.Dispose();
            process.Close();
        }
        catch (Exception ex){ throw ex; }
    }        
Arun Kumar
źródło