Descompacte arquivos programaticamente em .net

221

Estou tentando descompactar programaticamente um arquivo compactado.

Eu tentei usar a System.IO.Compression.GZipStreamclasse no .NET, mas quando meu aplicativo é executado (na verdade, um teste de unidade), recebo esta exceção:

System.IO.InvalidDataException: o número mágico no cabeçalho GZip não está correto. Verifique se você está passando em um fluxo GZip.

Agora percebo que um .ziparquivo não é o mesmo que um .gzarquivo, e que GZipnão é o mesmo que Zip.

No entanto, como eu posso extrair o arquivo clicando duas vezes manualmente no arquivo compactado e, em seguida, clicando no botão "Extrair todos os arquivos", acho que também deve haver uma maneira de fazer isso no código.

Portanto, eu tentei usar Process.Start()com o caminho para o arquivo compactado como entrada. Isso faz com que meu aplicativo abra uma janela mostrando o conteúdo do arquivo compactado. Tudo bem, mas o aplicativo será instalado em um servidor sem ninguém por perto para clicar no botão "Extrair todos os arquivos".

Então, como faço para que meu aplicativo extraia os arquivos nos arquivos compactados?

Ou existe outra maneira de fazer isso? Prefiro fazê-lo em código, sem baixar aplicativos ou bibliotecas de terceiros; o departamento de segurança não gosta muito disso ...

Petteri
fonte
12
Seu departamento de segurança está mais feliz com você escrevendo seu próprio código do que usando uma biblioteca que foi depurada e vista por muitos olhos, presumivelmente? Você pode usar uma biblioteca E "fazê-lo em código" (obter o código-fonte e compilar você mesmo), mas vejo reinventar a roda como um problema maior do que qualquer problema de segurança causado pelo uso de uma biblioteca testada e verdadeira.
Jared Updike
10
@Jared - Quando a gerência tem uma idéia na cabeça ...
Steven Evers
4
Há menos risco para o departamento de segurança se você adquirir um produto de terceiros. Basta baixar DotNetZip e renomeá-lo "[inserir nome da empresa] .ziplibrary.dll"
Simon

Respostas:

59

Usamos o SharpZipLib com sucesso em muitos projetos. Sei que é uma ferramenta de terceiros, mas o código-fonte está incluído e pode fornecer algumas dicas se você optar por reinventar a roda aqui.

Chris Conway
fonte
3
Eu tentei usar o SharpZipLib e funcionou bem. Acho que vou ter que ver se a proibição contra libs e apss de terceiros é uma regra estrita ou mais uma linha de orientação.
Petteri
10
Eu não conheço a sua empresa, mas minha experiência sempre foi que é possível obter uma exceção a esse tipo de regra se você escrever uma descrição de caso comercial do motivo pelo qual deseja a exceção. Aponte a economia de custos v. DIY, bem como o fato de que a fonte pode ser examinada. Como alternativa, muitas vezes você pode obter permissão para usar a fonte, mesmo que ela não permita que você use a dll - basta compilá-la você mesmo (ou pelo menos as partes que você realmente precisa usar ...).
RolandTumble
Você não precisa usar bibliotecas externas para descompactar arquivos zip; pode usar o Shell32 do System32. Por favor, veja stackoverflow.com/a/43066281/948694
arturn 28/03
490

Com o .NET 4.5, você pode descompactar arquivos usando a estrutura .NET:

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);
    }
  }
}

O código acima foi obtido diretamente da documentação da Microsoft: http://msdn.microsoft.com/en-us/library/ms404280(v=vs.110).aspx

ZipFileestá contido na montagem System.IO.Compression.FileSystem. (Obrigado nateirvin ... veja o comentário abaixo)

bsara
fonte
118
BTW, ZipFileestá contido na montagem System.IO.Compression.FileSystem.
Nateirvin
73
O que significa que você precisa adicionar uma referência de DLL ao assembly da estrutura System.IO.Compression.FileSystem.dll.
Chris Schiffhauer
e os arquivos .rar. O código acima falha ao extrair arquivos .rar.
Raghu
1
Eu tentei isso na minha API asp.net core, ele leu bem a primeira entrada, mas na segunda entrada sempre dá erro A local file header is corrupt. Alguma coisa sobre isso?
SoftSan
Mesmo com @SoftSan. Eu também tenho esse erro. O que fazer?
Rich
101

Para .Net 4.5+

Nem sempre é desejado gravar o arquivo descompactado no disco. Como desenvolvedor do ASP.Net, eu teria que mexer nas permissões para conceder direitos para o meu aplicativo gravar no sistema de arquivos. Ao trabalhar com fluxos na memória, posso contornar tudo isso e ler os arquivos diretamente:

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

Como alternativa, você ainda pode gravar o arquivo descompactado no disco chamando ExtractToFile():

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

Para usar a ZipArchiveclasse, você precisará adicionar uma referência ao System.IO.Compressionespaço para nome e ao System.IO.Compression.FileSystem.

Mister Epic
fonte
8
Realmente levou o MSFT até o 4.5+ para adicionar um descompressor nativo?
John Peters
2
O @JohnPeters GZipStream foi adicionado novamente no .Net 2.0 ( msdn.microsoft.com/en-us/library/… ). No entanto, não facilitou o trabalho com vários arquivos em um arquivo na memória. O novo ZipArchiveobjeto se encaixa perfeitamente.
Mister Epic
1
Essa é uma alternativa particularmente boa porque permite descompactar sem usar o sistema de arquivos (no meu caso, estou trabalhando com recursos incorporados), e também não é uma extensão de terceiros.
ANeves
1
Por que devo usar um foreachloop para ExtractToFilequando posso usar ZipFile.ExtractToDirectory(inputFile, outputDir);Qual é a vantagem do primeiro método?
The Robô Fofo
1
no .NET 4.6.1 não consigo obter 'ZipArchive' de 'System.IO.Compression.FileSystem', alguma ideia?
Ravi Anand
55

Grátis e sem arquivos DLL externos. Tudo está em um arquivo CS. Um download é apenas o arquivo CS, outro download é um exemplo muito fácil de entender. Tentei hoje e não acredito como a configuração foi simples. Funcionou na primeira tentativa, sem erros, sem nada.

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

Lukas
fonte
Falou muito cedo! Quero inflar os arquivos de um fluxo de download http instantaneamente. Isso não funciona, pois está usando as operações Seek no fluxo :( Bem, graças ao código-fonte, posso escrever meu próprio ZipStream agora ...
oyophant
a melhor solução para o meu problema, já que estou escrevendo um aplicativo atualizador e não posso envolver nenhuma DLL no processo de extração desde então, eu teria que atualizá-las também .... isso é bom. obrigado!
Niklas #
27

Use a biblioteca DotNetZip em http://www.codeplex.com/DotNetZip

biblioteca de classes e conjunto de ferramentas para manipular arquivos zip. Use VB, C # ou qualquer linguagem .NET para criar, extrair ou atualizar facilmente arquivos zip ...

O DotNetZip funciona em PCs com o .NET Framework completo e também é executado em dispositivos móveis que usam o .NET Compact Framework. Crie e leia arquivos zip em VB, C # ou qualquer linguagem .NET ou qualquer ambiente de script ...

Se tudo o que você deseja é uma classe DeflateStream ou GZipStream melhor para substituir a que está incorporada na .NET BCL, a DotNetZip também a possui. O DeflateStream e o GZipStream do DotNetZip estão disponíveis em um assembly independente, com base em uma porta .NET do Zlib. Esses fluxos suportam níveis de compactação e oferecem desempenho muito melhor do que as classes internas. Há também um ZlibStream para concluir o conjunto (RFC 1950, 1951, 1952) ...

Sam Axe
fonte
1
Hmmm ... Mas isso é uma biblioteca de terceiros!
Petteri
30
Que observador da sua parte. A menos que você queira passar vários meses implementando seu próprio leitor de arquivos Zip, é a sua melhor opção.
7339 Sam Ax
Esta é uma maneira melhor do que SharpZipLib
Kugel
5
Você está me fazendo perguntas sobre uma resposta com quase 5 anos de idade. Pesquise. Tenho certeza que você encontrará uma resposta.
Sam Ax
2
@ PhilCooper Essa é uma pergunta muito antiga que eu recomendo usar o System.IO.Compression.ZipFile interno. IIRC Tive experiências muito ruins com o SharpZipLib no passado, com base na minha experiência de produzir milhares de zips em tempo real.
Kugel 28/02
9
String ZipPath = @"c:\my\data.zip";
String extractPath = @"d:\\myunzips";
ZipFile.ExtractToDirectory(ZipPath, extractPath);

Para usar a classe ZipFile, você deve adicionar uma referência ao assembly System.IO.Compression.FileSystem no seu projeto

Mahadev Mane
fonte
1
Fonte: msdn.microsoft.com/en-us/library/…
gkubed 3/17/17
2

Arquivos zip padrão normalmente usam o algoritmo deflate.

Para extrair arquivos sem usar bibliotecas de terceiros, use DeflateStream. Você precisará de um pouco mais de informações sobre o formato de arquivo compactado, pois a Microsoft fornece apenas o algoritmo de compactação.

Você também pode tentar usar o zipfldr.dll. É a biblioteca de compactação da Microsoft (pastas compactadas no menu Enviar para). Parece ser uma biblioteca de com mas não documentada. Você poderá fazê-lo funcionar por meio de experimentação.

Kenneth Cochran
fonte
Estou experimentando a classe DeflateStream. Desta vez eu recebo System.IO.InvalidDataException: Bloco comprimento não coincide com o seu complemento ..
Petteri
Como eu disse acima, a Microsoft forneceu apenas o algoritmo. Você também precisará de informações sobre o formato de arquivo zip. pt.wikipedia.org/wiki/ZIP_(file_format) deve começar. Veja as referências na parte inferior da página para obter links para informações mais detalhadas.
5999 Kenneth Cochran
2
Eu também deparei com System.IO.Packaging.Package no .NET 3.5. Parece que pode fazer o truque, embora não seja muito intuitivo.
Kenneth Cochran
2

Eu uso isso para compactar ou descompactar vários arquivos. O material Regex não é obrigatório, mas eu o uso para alterar o carimbo de data e remover os sublinhados indesejados. Eu uso a string vazia na string Compress >> zipPath para prefixar algo em todos os arquivos, se necessário. Além disso, costumo comentar Compress () ou Decompress () com base no que estou fazendo.

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
fonte
Isso requer o dot net 4.5 - apenas uma observação, como notaram outros que responderam com o ZipFile e ainda estou usando o 3.5.
Thronk
2

Isso fará isso System.IO.Compression.ZipFile.ExtractToDirectory(ZipName, ExtractToPath)

Ilya Kochetov
fonte
1

A partir daqui :

Objetos compactados GZipStream gravados em um arquivo com uma extensão .gz podem ser descompactados usando muitas ferramentas de compactação comuns; no entanto, essa classe não fornece inerentemente funcionalidade para adicionar ou extrair arquivos de arquivos .zip.

RedWolves
fonte
1

Você pode fazer tudo isso no .NET 3.5 usando DeflateStream. O que falta no .NET 3.5 é a capacidade de processar as seções do cabeçalho do arquivo usadas para organizar os arquivos compactados. O PKWare publicou essas informações, que você pode usar para processar o arquivo zip depois de criar as estruturas usadas. Não é particularmente oneroso e é uma boa prática na criação de ferramentas sem o uso de código de terceiros.

Não é uma resposta de uma linha, mas é completamente factível se você estiver disposto e apto a dedicar algum tempo. Eu escrevi uma aula para fazer isso em algumas horas e o que obtive disso é a capacidade de compactar e descompactar arquivos usando apenas o .NET 3.5.

Michael Blake
fonte
0

Descobri esse (pacote de descompactação no NuGet) hoje, desde que deparei com um bug no DotNetZip, e percebi que não havia realmente tanto trabalho no DotNetZip nos últimos dois anos.

O pacote Unzip é enxuto e funcionou para mim - não tinha o bug que o DotNetZip tinha. Além disso, era um arquivo razoavelmente pequeno, contando com o Microsoft BCL para a descompactação real. Eu poderia facilmente fazer os ajustes necessários (para acompanhar o progresso durante a descompressão). Eu recomendo.

Per Lundberg
fonte
0

De recursos incorporados:

using (Stream _pluginZipResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(programName + "." + "filename.zip"))
{
    using (ZipArchive zip = new ZipArchive(_pluginZipResourceStream))
    {
        zip.ExtractToDirectory(Application.StartupPath);
    }
}
Steve Rousseau
fonte
0

Até agora, eu estava usando processos cmd para extrair um arquivo .iso, copiá-lo para um caminho temporário do servidor e extraído em um pendrive. Recentemente, descobri que isso está funcionando perfeitamente com .iso's com menos de 10 GB. Para um iso como 29Gb, esse método fica preso de alguma forma.

    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
fonte
0

você pode usar o Info-descompactar a linha de comando cod.you só precisa baixar o unzip.exe no site oficial do 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
fonte