IRONSOFTWAREHOME

Async Zip no .NET 10: Criação ou Extração de Uma Linha

Async Zip in .NET 10: One Line Create or Extract

Tim Corey

~12m

As operações de arquivo Zip em C# sempre foram possíveis através de System.IO.Compression, mas cada chamada era síncrona, significando que o thread era bloqueado até que o arquivo estivesse totalmente escrito ou lido. .NET 10 muda isso com um conjunto de sobrecargas assíncronas que permitem criar, extrair e popular arquivos zip sem prender o thread chamador.

Este tutorial demonstra as novas sobrecargas async zip no .NET 10, baseado no guia recente de Tim Corey. Veremos três abordagens cada vez mais detalhadas: uma linha única para criar um arquivo, uma única chamada para descompactá-lo e um método manual para controle total, enquanto também examinamos um problema comum com o escopo de using.

Configuração: Caminhos e a Pasta de Origem

[0:28 - 1:55] A configuração começa com um aplicativo de console direcionado a .NET 10 e uma única diretiva usando:

using System.IO.Compression;
C#

Três variáveis de string definem os caminhos usados durante a demonstração:

string sourceDirectory    = @"C:\temp\test";
string destinationZipFile = @"C:\temp\archive.zip";
string destinationDirectory = @"C:\temp\extracted";
C#

O prefixo de string literal (@) evita a necessidade de duplicar barras invertidas. sourceDirectory é a pasta para compactar. destinationZipFile é o caminho completo para o arquivo que será criado. destinationDirectory é onde os conteúdos serão colocados quando extraídos.

Um aviso prático: nunca aponte destinationZipFile para um caminho dentro de sourceDirectory. Escrever o zip na pasta que está sendo compactada causa um loop de leitura recursiva que quebra o processo.

A pasta de teste contém dois arquivos na raiz e um terceiro arquivo dentro de uma subpasta, o que importa ao explorar a opção includeBaseDirectory e o manuseio de caminhos relativos na abordagem manual.

Criando um Arquivo em Uma Linha

[2:35 - 4:20] A chamada de criação async é uma substituição direta para o síncrono ZipFile.CreateFromDirectory:

await ZipFile.CreateFromDirectoryAsync(
    sourceDirectory,
    destinationZipFile,
    CompressionLevel.SmallestSize,
    includeBaseDirectory: false);
C#

CompressionLevel.SmallestSize prioriza o menor output ao custo de um pouco mais de tempo de processamento. CompressionLevel.Fastest faz o inverso. Para a maioria dos cenários de desenvolvimento, a diferença é insignificante, mas em um servidor web processando muitos arquivos ao mesmo tempo, a troca precisa ser avaliada.

includeBaseDirectory controla se o nome do diretório de origem aparece como uma entrada raiz dentro do arquivo. Com false (o padrão), o zip abre diretamente para os arquivos. Passar true em vez disso coloca uma pasta test na raiz, com os arquivos reais dentro dela. A maioria dos casos de uso se beneficia ao definir isso para false.

Extraindo um Arquivo em Uma Linha

[4:45 - 5:55] A extração segue o mesmo padrão:

await ZipFile.ExtractToDirectoryAsync(
    destinationZipFile,
    destinationDirectory,
    overwriteFiles: false);
C#

destinationDirectory é criado automaticamente se ainda não existir. O parâmetro overwriteFiles tem como padrão false, que lança uma exceção se qualquer arquivo no arquivo já existir no caminho de destino. Defina para true para substituir arquivos existentes silenciosamente. Executar a extração duas vezes ilustra isso: a primeira passa com sucesso e cria a pasta, enquanto a segunda lança uma exceção a menos que overwriteFiles: true seja especificado.

Compressão Seletiva: Adicionando Arquivos Um por Um

[6:15 - 9:50] A abordagem de uma linha compacta um diretório inteiro sem filtragem. Quando você precisa incluir apenas arquivos específicos, você constrói o arquivo manualmente usando um FileStream e ZipArchive:

await using FileStream zipStream = new FileStream(
    destinationZipFile,
    FileMode.Create,
    FileAccess.Write,
    FileShare.None,
    bufferSize: 4096,
    useAsync: true);

using ZipArchive archive = await ZipArchive.CreateAsync(
    zipStream,
    ZipArchiveMode.Create,
    leaveOpen: false,
    entryNameEncoding: null);
C#

Alguns parâmetros aqui valem a pena ser compreendidos. FileMode.Create substitui qualquer arquivo existente nesse caminho. FileMode.CreateNew lança uma exceção se o arquivo já existir. FileShare.None bloqueia o arquivo exclusivamente enquanto o arquivo está sendo escrito, impedindo outros processos de lê-lo ou gravá-lo durante a operação.

useAsync: true no FileStream habilita E/S assíncrona no nível do sistema operacional. Observe que definir isso sem realmente usar chamadas assíncronas a jusante pode desacelerar o processo significativamente, às vezes em até dez vezes. Porque o loop de escrita de entrada usa await, useAsync: true é a escolha correta aqui. Em um caminho de código síncrono, deixe na configuração padrão false.

leaveOpen: false no ZipArchive indica para fechar e liberar o fluxo subjacente quando ele for descartado. entryNameEncoding: null mantém a codificação padrão, que a documentação recomenda manter, a menos que você tenha um motivo específico para alterá-la.

Com o arquivo pronto, recupere o conteúdo de origem e escreva cada entrada:

string[] files = Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories);

foreach (string filePath in files)
{
    string relativePathAndName = Path.GetRelativePath(sourceDirectory, filePath);
    await archive.CreateEntryFromFileAsync(filePath, relativePathAndName);
}
C#

Directory.GetFiles com SearchOption.AllDirectories recupera arquivos de subpastas aninhadas, o que é como a estrutura de subpastas é preservada no arquivo. O argumento de padrão ("*") é onde você filtra por extensão: "*.txt" limitaria o arquivo a arquivos de texto, por exemplo.

Path.GetRelativePath remove o prefixo absoluto de cada caminho de arquivo, deixando apenas a parte relativa a sourceDirectory. Isso é o que é armazenado como o nome da entrada dentro do zip, reproduzindo fielmente a hierarquia de subpastas. Se você passar Path.GetFileName(filePath) em vez disso, cada entrada será colocada na raiz do zip, independentemente de sua localização original. Isso produz um arquivo plano, mas corre o risco de uma colisão de nomes se duas entradas compartilharem um nome de arquivo em diferentes subpastas.

A Armadilha do Using Escopado

[9:50 - 11:30] Se você tentar adicionar uma chamada de extração após o bloco zip manual, encontrará uma exceção de bloqueio de arquivo: The process cannot access the file because it is being used by another process. Isso acontece porque a declaração using em zipStream usa sintaxe de escopo de arquivo, o que significa que o fluxo permanece aberto até o final do arquivo em vez de fechar na chave do bloco zip. A tentativa de extração é executada enquanto o bloqueio ainda está ativo.

Converter para uma forma com chave resolve isso:

// Before (file-scoped: stream stays open until end of file)
await using FileStream zipStream = new FileStream(...);

// After (block-scoped; stream released at the closing brace)
await using (FileStream zipStream = new FileStream(...))
{
    // zip operations here
}
// stream is now closed; extraction can proceed safely
C#

Colocar o trabalho de compactação entre chaves e remover o ponto e vírgula final limita a vida útil do stream a esse escopo explícito. Uma vez que a execução deixa a chave de fechamento, o bloqueio é liberado e uma chamada de extração subsequente pode abrir o mesmo arquivo sem conflito.

Conclusão

[11:40 - fim] As linhas únicas tratam dos casos comuns: CreateFromDirectoryAsync para arquivar uma pasta inteira e ExtractToDirectoryAsync para descompactá-la. Quando você precisa de controle sobre quais arquivos entram no arquivo, a abordagem manual FileStream e ZipArchive oferece filtragem, renomeação e reescrita de caminho no nível de entrada.

Para recapitular: adicione using System.IO.Compression, chame await ZipFile.CreateFromDirectoryAsync ou await ZipFile.ExtractToDirectoryAsync para casos diretos e recorra ao caminho manual ZipArchive quando precisar filtrar ou renomear entradas. Enquadre seus blocos using com chaves se o fluxo precisar ser liberado antes que o código subsequente seja executado. Estas adições tornam os padrões async/await disponíveis durante todo o fluxo de trabalho zip em .NET 10.

Assista ao vídeo completo no canal do Tim Corey no YouTube para acompanhar a programação ao vivo.

Earn More by Sharing What You Love

Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Let's Stay in Touch!

Join our newsletter, you’ll get exclusive access on article updates. We value your privacy

Key in blue circle

Obtenha sua chave de avaliação gratuita de 30 dias instantaneamente.

Your trial license will be sent to your email address

Sem limitações. 100% desbloqueado. Sem cartão de crédito.

bullet_checkedNão é necessário cartão de crédito nem criação de conta.Sem limitações. 100% desbloqueado. Sem cartão de crédito.
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
Agende sua consulta sem compromisso.
Preencha o formulário abaixo ou envie um e-mail para sales@ironsoftware.com
Os seus dados serão sempre mantidos em sigilo.
Aprovado por milhões de engenheiros em todo o mundo.
Logotipos dos clientes da Iron Software
Obtenha sua chave de avaliação gratuita de 30 dias instantaneamente.
Não é necessário cartão de crédito nem criação de conta.