HOW TO:壓縮與解壓縮檔案How to: Compress and extract files

System.IO.Compression 命名空間包含壓縮及解壓縮檔案和資料流的下列類型。The System.IO.Compression namespace contains the following types for compressing and decompressing files and streams. 您也可以使用這些類型讀取和修改壓縮檔案的內容。You can also use these types to read and modify the contents of a compressed file.

下列範例示範您可以搭配壓縮檔案執行的部分作業。The following examples show some of the operations you can perform with compressed files.

範例 1:建立和解壓縮 .zip 檔案Example 1: Create and extract a .zip file

下列範例示範如何使用 ZipFile 類別,建立和解壓縮 .zip 檔案。The following example shows how to create and extract a compressed .zip file by using the ZipFile class. 此範例會將資料夾的內容壓縮至新的 .zip 檔案,然後將該 zip 解壓縮至新的資料夾。The example compresses the contents of a folder into a new .zip file, and then extracts the zip to a new folder.

若要執行樣本,請在程式資料夾中建立啟動資料夾,並在其中填入要壓縮的檔案。To run the sample, create a start folder in your program folder and populate it with files to zip.

如果您收到組建錯誤「名稱 'ZipFile' 不存在於目前的內容中」,請將 System.IO.Compression.FileSystem 組件的參考新增至您的專案。If you get the build error "The name 'ZipFile' does not exist in the current context," add a reference to the System.IO.Compression.FileSystem assembly to your project.

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

範例 2:解壓縮特定的副檔名Example 2: Extract specific file extensions

下一個範例會逐一查看現有 .zip 檔案的內容,並將副檔名為 .txt 的檔案解壓縮。The next example iterates through the contents of an existing .zip file and extracts files that have a .txt extension. 它會使用 ZipArchive 類別來存取 zip,以及使用 ZipArchiveEntry 類別來檢查個別項目。It uses the ZipArchive class to access the zip, and the ZipArchiveEntry class to inspect the individual entries. ZipArchiveEntry 物件的擴充方法 ExtractToFile 可在 System.IO.Compression.ZipFileExtensions 類別中使用。The extension method ExtractToFile for the ZipArchiveEntry object is available in the System.IO.Compression.ZipFileExtensions class.

若要執行樣本,請將名為 result.zip.zip 檔案置於您的程式資料夾中。To run the sample, place a .zip file called result.zip in your program folder. 出現提示時,提供要擷取至的資料夾名稱。When prompted, provide a folder name to extract to.

如果您收到組建錯誤「名稱 'ZipFile' 不存在於目前的內容中」,請將 System.IO.Compression.FileSystem 組件的參考新增至您的專案。If you get the build error "The name 'ZipFile' does not exist in the current context," add a reference to the System.IO.Compression.FileSystem assembly to your project.

如果您收到錯誤「類型 'ZipArchive' 定義在未參考的組件中」,請將 System.IO.Compression 組件的參考新增至您的專案。If you get the error "The type 'ZipArchive' is defined in an assembly that is not referenced," add a reference to the System.IO.Compression assembly to your project.

重要

當您在解壓縮檔案時,必須尋找惡意的檔案路徑,以免它逸出您解壓縮的目的目錄。When unzipping files, you must look for malicious file paths, which can escape out of the directory you unzip into. 這是所謂的路徑周遊攻擊。This is known as a path traversal attack. 以下範例示範如何檢查惡意的檔案路徑,並提供安全的方式來解壓縮。The following example demonstrates how to check for malicious file paths and provides a safe way to unzip.

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

範例 3:將檔案新增至現有 zipExample 3: Add a file to an existing zip

下列範例使用 ZipArchive 類別存取現有的 .zip 檔案,並將檔案新增至其中。The following example uses the ZipArchive class to access an existing .zip file, and adds a file to it. 將新檔案新增至現有的 .zip 時,即會壓縮該檔案。The new file gets compressed when you add it to the existing zip.

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

範例 4:壓縮和解壓縮 .gz 檔案Example 4: Compress and decompress .gz files

您也可以使用 GZipStreamDeflateStream 類別壓縮和解壓縮資料。You can also use the GZipStream and DeflateStream classes to compress and decompress data. 它們會使用相同的壓縮演算法。They use the same compression algorithm. 您可以使用許多常用工具,將寫入至 .gz 檔案的 GZipStream 物件解壓縮。You can decompress GZipStream objects that are written to a .gz file by using many common tools. 以下範例示範如何使用 GZipStream 類別壓縮和解壓縮檔案目錄:The following example shows how to compress and decompress a directory of files by using the GZipStream class:

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

另請參閱See also