Procedimiento para comprimir y extraer archivos
El espacio de nombres System.IO.Compression contiene las siguientes clases para comprimir y descomprimir archivos y secuencias. También puede usar estos tipos para leer y modificar el contenido de un archivo comprimido:
Los siguientes ejemplos muestran algunas de las operaciones que se pueden ejecutar con archivos comprimidos. En estos ejemplos es necesario que los siguientes paquetes de NuGet se agreguen al proyecto:
Si se usa .NET Framework, agregue referencias a estas dos bibliotecas al proyecto:
System.IO.Compression
System.IO.Compression.FileSystem
Ejemplo 1: Crear y extraer un archivo .zip
En este ejemplo se muestra cómo crear y extraer un archivo comprimido .zip mediante la clase ZipFile. El ejemplo comprime el contenido de una carpeta en un archivo .zip nuevo y, a continuación, extrae el archivo zip a una nueva carpeta.
Para ejecutar el ejemplo, cree una carpeta de inicio en su carpeta de programa y rellénela con archivos que se van a comprimir.
using System;
using System.IO.Compression;
class Program
{
static void Main(string[] args)
{
string startPath = @".\start";
string zipPath = @".\result.zip";
string extractPath = @".\extract";
ZipFile.CreateFromDirectory(startPath, zipPath);
ZipFile.ExtractToDirectory(zipPath, extractPath);
}
}
Imports System.IO.Compression
Module Module1
Sub Main()
Dim startPath As String = ".\start"
Dim zipPath As String = ".\result.zip"
Dim extractPath As String = ".\extract"
ZipFile.CreateFromDirectory(startPath, zipPath)
ZipFile.ExtractToDirectory(zipPath, extractPath)
End Sub
End Module
Ejemplo 2: Extraer determinadas extensiones de archivo
El ejemplo siguiente itera el contenido de un archivo .zip existente y extrae archivos que tienen una extensión .txt. Usa la clase ZipArchive para tener acceso al archivo .zip y la clase ZipArchiveEntry para inspeccionar las entradas individuales. El método de extensión ExtractToFile para el objeto ZipArchiveEntry está disponible en la clase System.IO.Compression.ZipFileExtensions.
Para ejecutar el ejemplo, coloque un archivo .zip llamado result.zip en su carpeta de programa. Cuando se le pida, proporcione un nombre de carpeta al que se va a extraer.
Importante
Al descomprimir archivos, también debe buscar rutas de acceso de archivos malintencionados que puedan escapar del directorio en el que descomprime. Esto se conoce como un ataque de ruta transversal. En el ejemplo siguiente se muestra cómo comprobar si hay rutas de acceso a archivos malintencionados y se proporciona una manera segura para descomprimir.
using System;
using System.IO;
using System.IO.Compression;
class Program
{
static void Main(string[] args)
{
string zipPath = @".\result.zip";
Console.WriteLine("Provide path where to extract the zip file:");
string extractPath = Console.ReadLine();
// Normalizes the path.
extractPath = Path.GetFullPath(extractPath);
// Ensures that the last character on the extraction path
// is the directory separator char.
// Without this, a malicious zip file could try to traverse outside of the expected
// extraction path.
if (!extractPath.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
extractPath += Path.DirectorySeparatorChar;
using (ZipArchive archive = ZipFile.OpenRead(zipPath))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.FullName.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
{
// Gets the full path to ensure that relative segments are removed.
string destinationPath = Path.GetFullPath(Path.Combine(extractPath, entry.FullName));
// Ordinal match is safest, case-sensitive volumes can be mounted within volumes that
// are case-insensitive.
if (destinationPath.StartsWith(extractPath, StringComparison.Ordinal))
entry.ExtractToFile(destinationPath);
}
}
}
}
}
Imports System.IO
Imports System.IO.Compression
Module Module1
Sub Main()
Dim zipPath As String = ".\result.zip"
Console.WriteLine("Provide path where to extract the zip file:")
Dim extractPath As String = Console.ReadLine()
' Normalizes the path.
extractPath = Path.GetFullPath(extractPath)
' Ensures that the last character on the extraction path
' is the directory separator char.
' Without this, a malicious zip file could try to traverse outside of the expected
' extraction path.
If Not extractPath.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) Then
extractPath += Path.DirectorySeparatorChar
End If
Using archive As ZipArchive = ZipFile.OpenRead(zipPath)
For Each entry As ZipArchiveEntry In archive.Entries
If entry.FullName.EndsWith(".txt", StringComparison.OrdinalIgnoreCase) Then
' Gets the full path to ensure that relative segments are removed.
Dim destinationPath As String = Path.GetFullPath(Path.Combine(extractPath, entry.FullName))
' Ordinal match is safest, case-sensitive volumes can be mounted within volumes that
' are case-insensitive.
If destinationPath.StartsWith(extractPath, StringComparison.Ordinal) Then
entry.ExtractToFile(destinationPath)
End If
End If
Next
End Using
End Sub
End Module
Ejemplo 3: agregar un archivo a un archivo .zip existente
En el ejemplo siguiente se utiliza la clase ZipArchive para acceder a un archivo .zip existente y se agrega un archivo a este. El nuevo archivo se comprime cuando se agrega al archivo .zip existente.
using System;
using System.IO;
using System.IO.Compression;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
using (FileStream zipToOpen = new FileStream(@"c:\users\exampleuser\release.zip", FileMode.Open))
{
using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update))
{
ZipArchiveEntry readmeEntry = archive.CreateEntry("Readme.txt");
using (StreamWriter writer = new StreamWriter(readmeEntry.Open()))
{
writer.WriteLine("Information about this package.");
writer.WriteLine("========================");
}
}
}
}
}
}
Imports System.IO
Imports System.IO.Compression
Module Module1
Sub Main()
Using zipToOpen As FileStream = New FileStream("c:\users\exampleuser\release.zip", FileMode.Open)
Using archive As ZipArchive = New ZipArchive(zipToOpen, ZipArchiveMode.Update)
Dim readmeEntry As ZipArchiveEntry = archive.CreateEntry("Readme.txt")
Using writer As StreamWriter = New StreamWriter(readmeEntry.Open())
writer.WriteLine("Information about this package.")
writer.WriteLine("========================")
End Using
End Using
End Using
End Sub
End Module
Ejemplo 4: Comprimir y descomprimir archivos .gz
También puede usar las clases GZipStream y DeflateStream para comprimir y descomprimir los datos. Usan el mismo algoritmo de compresión. Puede descomprimir objetos GZipStream que se escriben en un archivo .gz mediante muchas herramientas comunes. En este ejemplo se muestra cómo comprimir y descomprimir un directorio de archivos con la clase GZipStream:
using System;
using System.IO;
using System.IO.Compression;
public class Program
{
private static string directoryPath = @".\temp";
public static void Main()
{
DirectoryInfo directorySelected = new DirectoryInfo(directoryPath);
Compress(directorySelected);
foreach (FileInfo fileToDecompress in directorySelected.GetFiles("*.gz"))
{
Decompress(fileToDecompress);
}
}
public static void Compress(DirectoryInfo directorySelected)
{
foreach (FileInfo fileToCompress in directorySelected.GetFiles())
{
using (FileStream originalFileStream = fileToCompress.OpenRead())
{
if ((File.GetAttributes(fileToCompress.FullName) &
FileAttributes.Hidden) != FileAttributes.Hidden & fileToCompress.Extension != ".gz")
{
using (FileStream compressedFileStream = File.Create(fileToCompress.FullName + ".gz"))
{
using (GZipStream compressionStream = new GZipStream(compressedFileStream,
CompressionMode.Compress))
{
originalFileStream.CopyTo(compressionStream);
}
}
FileInfo info = new FileInfo(directoryPath + Path.DirectorySeparatorChar + fileToCompress.Name + ".gz");
Console.WriteLine($"Compressed {fileToCompress.Name} from {fileToCompress.Length.ToString()} to {info.Length.ToString()} bytes.");
}
}
}
}
public static void Decompress(FileInfo fileToDecompress)
{
using (FileStream originalFileStream = fileToDecompress.OpenRead())
{
string currentFileName = fileToDecompress.FullName;
string newFileName = currentFileName.Remove(currentFileName.Length - fileToDecompress.Extension.Length);
using (FileStream decompressedFileStream = File.Create(newFileName))
{
using (GZipStream decompressionStream = new GZipStream(originalFileStream, CompressionMode.Decompress))
{
decompressionStream.CopyTo(decompressedFileStream);
Console.WriteLine($"Decompressed: {fileToDecompress.Name}");
}
}
}
}
}
Imports System.IO
Imports System.IO.Compression
Module Module1
Private directoryPath As String = ".\temp"
Public Sub Main()
Dim directorySelected As New DirectoryInfo(directoryPath)
Compress(directorySelected)
For Each fileToDecompress As FileInfo In directorySelected.GetFiles("*.gz")
Decompress(fileToDecompress)
Next
End Sub
Public Sub Compress(directorySelected As DirectoryInfo)
For Each fileToCompress As FileInfo In directorySelected.GetFiles()
Using originalFileStream As FileStream = fileToCompress.OpenRead()
If (File.GetAttributes(fileToCompress.FullName) And FileAttributes.Hidden) <> FileAttributes.Hidden And fileToCompress.Extension <> ".gz" Then
Using compressedFileStream As FileStream = File.Create(fileToCompress.FullName & ".gz")
Using compressionStream As New GZipStream(compressedFileStream, CompressionMode.Compress)
originalFileStream.CopyTo(compressionStream)
End Using
End Using
Dim info As New FileInfo(directoryPath & Path.DirectorySeparatorChar & fileToCompress.Name & ".gz")
Console.WriteLine($"Compressed {fileToCompress.Name} from {fileToCompress.Length.ToString()} to {info.Length.ToString()} bytes.")
End If
End Using
Next
End Sub
Private Sub Decompress(ByVal fileToDecompress As FileInfo)
Using originalFileStream As FileStream = fileToDecompress.OpenRead()
Dim currentFileName As String = fileToDecompress.FullName
Dim newFileName = currentFileName.Remove(currentFileName.Length - fileToDecompress.Extension.Length)
Using decompressedFileStream As FileStream = File.Create(newFileName)
Using decompressionStream As GZipStream = New GZipStream(originalFileStream, CompressionMode.Decompress)
decompressionStream.CopyTo(decompressedFileStream)
Console.WriteLine($"Decompressed: {fileToDecompress.Name}")
End Using
End Using
End Using
End Sub
End Module