C # realizes Zip compression of files

Posted by sasquatch69 on Fri, 21 Jan 2022 14:04:01 +0100

Original address: https://www.jb51.net/article/216176.htm

 

1, Single file compression

In the scenario, the file may be large and needs to be compressed for transmission, such as uploading and downloading

Note: in line 26, the blocksize is the size of the cache. It cannot be set too large. If it is too large, an exception will be reported. Lines 26-38, read the file into the buffer through the FileStream stream, and then write it to the ZipOutputStream stream stream. You can imagine two pipes, one reading and the other writing, with buffers in the middle. Their working mode is synchronous. Think about it. Can it work asynchronously? The reading pipeline just reads and the writing pipeline just writes? In such a scenario, if the reading is very fast and the writing is relatively slow, for example, it is not written locally, but needs to be transmitted through the network, the asynchronous mode can be considered. How to do it, readers can transform themselves. The key point is that the flow is sequential, so you should ensure the correctness of the sequence

/// <summary>
/// Single file compression
/// </summary>
/// <param name="sourceFile">source file</param>
/// <param name="zipedFile">zip Compressed file</param>
/// <param name="blockSize">Buffer size</param>
/// <param name="compressionLevel">Compression level</param>
public static void ZipFile(string sourceFile, string zipedFile, int blockSize = 1024, int compressionLevel = 6)
{
    if (!File.Exists(sourceFile))
    {
        throw new System.IO.FileNotFoundException("The specified file " + sourceFile + " could not be found.");
    }
    var fileName = System.IO.Path.GetFileNameWithoutExtension(sourceFile);
 
    FileStream streamToZip = new FileStream(sourceFile, FileMode.Open, FileAccess.Read);
    FileStream zipFile = File.Create(zipedFile);
    ZipOutputStream zipStream = new ZipOutputStream(zipFile);
 
    ZipEntry zipEntry = new ZipEntry(fileName);
    zipStream.PutNextEntry(zipEntry);
 
    //Storage, fastest, faster, standard, better, best 0-9
    zipStream.SetLevel(compressionLevel);
 
    byte[] buffer = new byte[blockSize];
 
    int size = streamToZip.Read(buffer, 0, buffer.Length);
    zipStream.Write(buffer, 0, size);
    try
    {
        while (size < streamToZip.Length)
        {
            int sizeRead = streamToZip.Read(buffer, 0, buffer.Length);
            zipStream.Write(buffer, 0, sizeRead);
            size += sizeRead;
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
    zipStream.Finish();
    zipStream.Close();
    streamToZip.Close();
}

 

2, Multi file compression

This kind of scenario is also quite common. Similar to single file compression, it is just to cycle several times.

/// <summary>
/// Multi file compression
/// </summary>
/// <param name="zipfile">zip Compressed file</param>
/// <param name="filenames">Source file collection</param>
/// <param name="password">Compression encryption</param>
public void ZipFiles(string zipfile, string[] filenames, string password = "")
{
    ZipOutputStream s = new ZipOutputStream(System.IO.File.Create(zipfile));
 
    s.SetLevel(6);
 
    if (password != "")
        s.Password = Md5Help.Encrypt(password);
 
    foreach (string file in filenames)
    {
        //Open compressed file
        FileStream fs = File.OpenRead(file);
 
        byte[] buffer = new byte[fs.Length];
        fs.Read(buffer, 0, buffer.Length);
 
        var name = Path.GetFileName(file);
 
        ZipEntry entry = new ZipEntry(name);
        entry.DateTime = DateTime.Now;
        entry.Size = fs.Length;
        fs.Close();
        s.PutNextEntry(entry);
        s.Write(buffer, 0, buffer.Length);
    }
    s.Finish();
    s.Close();
}

Note: in line 21, the buffer size is directly the file size, so there is no circular reading and writing after reading at one time. In this case, a single file cannot be too large, such as more than 1G. In line 14, you can set the password for the compressed package. The generation method of MD5 is as follows:

public class Md5Help
{
    /// <summary>
    ///32 position MD5 encryption
    /// </summary>
    /// <param name="str">Encrypted character</param>
    /// <returns></returns>
    public static string Encrypt(string str)
    {
        MD5 md5 = new MD5CryptoServiceProvider();
 
        byte[] encryptdata = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
 
        return Convert.ToBase64String(encryptdata);
    }
}

 

3, Multi file asynchronous compression

The premise of the above synchronous compression is that the files are small and the number of files is small, but the reality is that not only the files are large, but also the number of files is large. In this case, asynchronous methods should be considered. Otherwise, the main thread will be blocked, which is what we usually call stuck

/// <summary>
/// Asynchronously compressed files are zip Compressed package
/// </summary>
/// <param name="zipfile">Compressed package storage path</param>
/// <param name="filenames">File collection</param>
public static async void ZipFilesAsync(string zipfile, string[] filenames)
{
    await Task.Run(() =>
    {
        ZipOutputStream s = null;
        try
        {
            s = new ZipOutputStream(System.IO.File.Create(zipfile));
 
            s.SetLevel(6); // 0 - store only to 9 - means best compression 
 
            foreach (string file in filenames)
            {
                //Open compressed file 
                FileStream fs = System.IO.File.OpenRead(file);
 
                var name = Path.GetFileName(file);
                ZipEntry entry = new ZipEntry(name);
                entry.DateTime = DateTime.Now;
                entry.Size = fs.Length;
                s.PutNextEntry(entry);
 
                //If the file is greater than 1 G
                long blockSize = 51200;
 
                var size = (int)fs.Length;
 
                var oneG = 1024 * 1024 * 1024;
 
                if (size > oneG)
                {
                    blockSize = oneG;
                }
                byte[] buffer = new byte[blockSize];
 
                size = fs.Read(buffer, 0, buffer.Length);
 
                s.Write(buffer, 0, size);
 
                while (size < fs.Length)
                {
                    int sizeRead = fs.Read(buffer, 0, buffer.Length);
                    s.Write(buffer, 0, sizeRead);
                    size += sizeRead;
                }
                s.Flush();
                fs.Close();
            }
 
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error compressing file asynchronously:" + ex.Message);
        }
        finally
        {
            s?.Finish();
            s?.Close();
        }
    });
}

 

4, Compressed folder

In practical applications, files and folders are compressed together, so in this case, just put all the things to be compressed into one folder and then compress them.

The main method is as follows:

/// <summary>
/// Asynchronously compressed folder is zip Compressed package
/// </summary>
/// <param name="zipfile">Compressed package storage path</param>
/// <param name="sourceFolder">Compressed package storage path</param>
/// <param name="filenames">File collection</param>
public static async void ZipFolderAsync(string zipfile, string sourceFolder, string[] filenames)
{
    await Task.Run(() =>
    {
        ZipOutputStream s = null;
        try
        {
            s = new ZipOutputStream(System.IO.File.Create(zipfile));
 
            s.SetLevel(6); // 0 - store only to 9 - means best compression 
 
            CompressFolder(sourceFolder, s, sourceFolder);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error compressing file asynchronously:" + ex.Message);
        }
        finally
        {
            s?.Finish();
            s?.Close();
        }
    });
}

Core method of compression:

 

/// <summary>
/// Compressed folder
/// </summary>
/// <param name="source">Source directory</param>
/// <param name="s">ZipOutputStream object</param>
/// <param name="parentPath">and source identical</param>
public static void CompressFolder(string source, ZipOutputStream s, string parentPath)
{
    string[] filenames = Directory.GetFileSystemEntries(source);
    foreach (string file in filenames)
    {
        if (Directory.Exists(file))
        {
            CompressFolder(file, s, parentPath);  //Recursively compress subfolders
        }
        else
        {
            using (FileStream fs = System.IO.File.OpenRead(file))
            {
                var writeFilePath = file.Replace(parentPath, "");
                ZipEntry entry = new ZipEntry(writeFilePath);
                entry.DateTime = DateTime.Now;
                entry.Size = fs.Length;
 
                s.PutNextEntry(entry);
 
                //If the file is greater than 1 G
                long blockSize = 51200;
 
                var size = (int)fs.Length;
 
                var oneG = 1024 * 1024 * 1024;
 
                if (size > oneG)
                {
                    blockSize = oneG;
                }
                byte[] buffer = new byte[blockSize];
 
                size = fs.Read(buffer, 0, buffer.Length);
 
                s.Write(buffer, 0, size);
 
 
                while (size < fs.Length)
                {
                    int sizeRead = fs.Read(buffer, 0, buffer.Length);
                    s.Write(buffer, 0, sizeRead);
                    size += sizeRead;
                }
 
                s.Flush();   //Clear the buffer of the stream so that all buffered data is written to the file
                fs.Close();
            }
        }
    }
}

The only thing to note is that the decompressed directory structure may be different from the file directory before compression. At this time, check the parentPath parameter, which is used when ZipEntry entity new, and replace the absolute path with the current relative path, that is, the path of the relative compressed folder.

The above method is complex. There is also a relatively simple way to directly call the api:

public static string ZipFolder(string sourceFolder, string zipFile)
{
    string result = "";
    try
    {
        //Create a compressed package
        if (!Directory.Exists(sourceFolder)) return result = "Compressed folder does not exist";
 
        DirectoryInfo d = new DirectoryInfo(sourceFolder);
        var files = d.GetFiles();
        if (files.Length == 0)
        {
            //Find subdirectory
            var ds = d.GetDirectories();
            if (ds.Length > 0)
            {
                files = ds[0].GetFiles();
            }
        }
        if (files.Length == 0) return result = "The file to be compressed is empty";
        System.IO.Compression.ZipFile.CreateFromDirectory(sourceFolder, zipFile);
    }
    catch (Exception ex)
    {
        result += "Compression error:" + ex.Message;
    }
    return result;
}

 

Topics: C#