it-swarm.com.ru

Создать нормальный Zip файл программно

Я видел много учебников о том, как сжать один файл в C #. Но мне нужно иметь возможность создать обычный * .Zip-файл из более чем одного файла. Есть ли в .NET что-нибудь, что может это сделать? Что бы вы предложили (если учесть, что я нахожусь под строгими правилами и не могу использовать другие библиотеки)

Спасибо

33
anon271334

Правка: если вы используете .Net 4.5 или более поздней версии, это встроенный в каркас

Для более ранних версий или для большего контроля вы можете использовать функции оболочки Windows, как описано здесь, в CodeProject, автор Gerald Gibson Jr .

Я скопировал текст статьи ниже, как написано (оригинальная лицензия: общественное достояние )

Сжатие Zip-файлов с помощью Windows Shell API и C

 enter image description here

Вступление

Это продолжение статьи, написанной мной о распаковке Zip-файлов. С помощью этого кода вы можете использовать Windows Shell API в C # для сжатия Zip-файлов и делать это без отображения окна «Ход выполнения копирования», показанного выше. Обычно, когда вы используете Shell API для сжатия Zip-файла, он будет отображать окно «Ход выполнения копирования», даже если вы установили параметры, чтобы запретить Windows показывать его. Чтобы обойти это, вы перемещаете код API оболочки в отдельный исполняемый файл, а затем запускаете этот исполняемый файл, используя класс .NET Process, обязательно устанавливая стиль окна процесса на «Скрытый».

Фон

Всегда нужно было сжимать файлы Zip и нужен лучший Zip, чем то, что поставляется со многими бесплатными библиотеками сжатия? То есть вам нужно было сжать папки и подпапки, а также файлы. Windows Zipping может сжимать не только отдельные файлы. Все, что вам нужно, это способ программно заставить Windows тихо сжать эти файлы Zip. Конечно, вы могли бы потратить 300 долларов на один из коммерческих компонентов Zip, но его трудно превзойти, если все, что вам нужно, - это сжать иерархии папок.

Используя код

В следующем коде показано, как использовать Windows Shell API для сжатия файла Zip. Сначала вы создаете пустой Zip-файл. Для этого создайте правильно сконструированный байтовый массив, а затем сохраните этот массив как файл с расширением .Zip. Как я узнал, какие байты поместить в массив? Ну, я просто использовал Windows, чтобы создать файл Zip с одним сжатым файлом внутри. Затем я открыл Zip с Windows и удалил сжатый файл. Это оставило меня с пустым почтовым индексом. Затем я открыл пустой Zip-файл в шестнадцатеричном редакторе (Visual Studio), посмотрел значения шестнадцатеричных байтов, преобразовал их в десятичные с помощью Windows Calc и скопировал эти десятичные значения в код моего байтового массива. Исходная папка указывает на папку, которую вы хотите сжать. Папка назначения указывает на пустой Zip-файл, который вы только что создали. Этот код, как есть, сожмет Zip-файл, однако он также покажет окно копирования. Чтобы этот код работал, вам также нужно установить ссылку на библиотеку COM. В окне «Ссылки» перейдите на вкладку «COM» и выберите библиотеку с надписью «Microsoft Shell Controls and Automation».

//Create an empty Zip file
byte[] emptyzip = new byte[]{80,75,5,6,0,0,0,0,0, 
                  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

FileStream fs = File.Create(args[1]);
fs.Write(emptyzip, 0, emptyzip.Length);
fs.Flush();
fs.Close();
fs = null;

//Copy a folder and its contents into the newly created Zip file
Shell32.ShellClass sc = new Shell32.ShellClass();
Shell32.Folder SrcFlder = sc.NameSpace(args[0]);
Shell32.Folder DestFlder = sc.NameSpace(args[1]); 
Shell32.FolderItems items = SrcFlder.Items();
DestFlder.CopyHere(items, 20);

//Ziping a file using the Windows Shell API 
//creates another thread where the zipping is executed.
//This means that it is possible that this console app 
//would end before the zipping thread 
//starts to execute which would cause the Zip to never 
//occur and you will end up with just
//an empty Zip file. So wait a second and give 
//the zipping thread time to get started
System.Threading.Thread.Sleep(1000);

Пример решения, включенного в эту статью, показывает, как поместить этот код в консольное приложение, а затем запустить это консольное приложение, чтобы сжать Zip без отображения окна «Копировать ход выполнения».

Приведенный ниже код показывает обработчик события нажатия кнопки, который содержит код, используемый для запуска консольного приложения, чтобы не было пользовательского интерфейса во время сжатия:

private void btnUnzip_Click(object sender, System.EventArgs e)
{
    //Test to see if the user entered a Zip file name
    if(txtZipFileName.Text.Trim() == "")
    {
        MessageBox.Show("You must enter what" + 
               " you want the name of the Zip file to be");
        //Change the background color to cue the user to what needs fixed
        txtZipFileName.BackColor = Color.Yellow;
        return;
    }
    else
    {
        //Reset the background color
        txtZipFileName.BackColor = Color.White;
    }

    //Launch the Zip.exe console app to do the actual zipping
    System.Diagnostics.ProcessStartInfo i =
        new System.Diagnostics.ProcessStartInfo(
        AppDomain.CurrentDomain.BaseDirectory + "Zip.exe");
    i.CreateNoWindow = true;
    string args = "";


    if(txtSource.Text.IndexOf(" ") != -1)
    {
        //we got a space in the path so wrap it in double qoutes
        args += "\"" + txtSource.Text + "\"";
    }
    else
    {
        args += txtSource.Text;
    }

    string dest = txtDestination.Text;

    if(dest.EndsWith(@"\") == false)
    {
        dest += @"\";
    }

    //Make sure the Zip file name ends with a Zip extension
    if(txtZipFileName.Text.ToUpper().EndsWith(".Zip") == false)
    {
        txtZipFileName.Text += ".Zip";
    }

    dest += txtZipFileName.Text;

    if(dest.IndexOf(" ") != -1)
    {
        //we got a space in the path so wrap it in double qoutes
        args += " " + "\"" + dest + "\"";
    }
    else
    {
        args += " " + dest;
    }

    i.Arguments = args;


    //Mark the process window as hidden so 
    //that the progress copy window doesn't show
    i.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;    
    System.Diagnostics.Process p = System.Diagnostics.Process.Start(i);
    p.WaitForExit();
    MessageBox.Show("Complete");
}
17
stuartd

Просто обновление по этому вопросу для всех, кто сталкивается с этим вопросом.

Начиная с .NET 4.5, вы можете сжимать каталог с помощью System.IO.Compression в Zip-файл. Вы должны добавить System.IO.Compression.FileSystem как ссылку, так как на нее нет ссылок по умолчанию. Тогда вы можете написать:

System.IO.Compression.ZipFile.CreateFromDirectory(dirPath, zipFile);

Единственная потенциальная проблема заключается в том, что эта сборка недоступна для приложений Магазина Windows.

60
Danny

Теперь вы можете использовать класс ZipArchive (System.IO.Compression.ZipArchive), доступный в .NET 4.5

Пример: создание Zip из PDF файлов

using (var fileStream = new FileStream(@"C:\temp\temp.Zip", FileMode.CreateNew))
{
    using (var archive = new ZipArchive(fileStream, ZipArchiveMode.Create, true))
    {
        foreach (var creditNumber in creditNumbers)
        {
            var pdfBytes = GeneratePdf(creditNumber);
            var fileName = "credit_" + creditNumber + ".pdf";
            var zipArchiveEntry = archive.CreateEntry(fileName, CompressionLevel.Fastest);
            using (var zipStream = zipArchiveEntry.Open())
                zipStream.Write(pdfBytes, 0, pdfBytes.Length);
            }
        }
    }
}
24
andersh

Вот несколько ресурсов, которые вы можете рассмотреть: Создание Zip-архивов в .NET (без внешней библиотеки, такой как SharpZipLib)

Архивируйте ваши потоки с System.IO.Packaging

Моя рекомендация и предпочтение было бы использовать system.io.packacking. Это ограничивает ваши зависимости (только инфраструктура). В публикации Jgalloway (первая ссылка) представлен хороший пример добавления двух файлов в Zip-файл. Да, это более многословно, но вы можете легко создать фасад (в некоторой степени это делает его AddFileToZip). 

НТН

20
Jake

Мои 2 цента:

    using (ZipArchive archive = ZipFile.Open(zFile, ZipArchiveMode.Create))
    {
        foreach (var fPath in filePaths)
        {
            archive.CreateEntryFromFile(fPath,Path.GetFileName(fPath));
        }
    }

Таким образом, файлы Zip могут быть созданы непосредственно из файлов/каталогов. 

17
Guy L

Вы можете попробовать SharpZipLib для этого. Это открытый исходный код, независимый от платформы чистый код C #.

6
codymanix

.NET имеет встроенную функциональность для сжатия файлов в пространстве имен System.IO.Compression . Используя это, вам не нужно брать дополнительную библиотеку в качестве зависимости. Эта функциональность доступна из .NET 2.0.

Вот способ сделать сжатие со страницы MSDN, на которую я ссылался: 

    public static void Compress(FileInfo fi)
    {
        // Get the stream of the source file.
        using (FileStream inFile = fi.OpenRead())
        {
            // Prevent compressing hidden and already compressed files.
            if ((File.GetAttributes(fi.FullName) & FileAttributes.Hidden)
                    != FileAttributes.Hidden & fi.Extension != ".gz")
            {
                // Create the compressed file.
                using (FileStream outFile = File.Create(fi.FullName + ".gz"))
                {
                    using (GZipStream Compress = new GZipStream(outFile,
                            CompressionMode.Compress))
                    {
                        // Copy the source file into the compression stream.
                        byte[] buffer = new byte[4096];
                        int numRead;
                        while ((numRead = inFile.Read(buffer, 0, buffer.Length)) != 0)
                        {
                            Compress.Write(buffer, 0, numRead);
                        }
                        Console.WriteLine("Compressed {0} from {1} to {2} bytes.",
                            fi.Name, fi.Length.ToString(), outFile.Length.ToString());
                    }
                }
            }
        }
4
gyurisc

Вы должны посмотреть в Zip Packages

Это более структурированная версия обычных Zip-архивов, требующая некоторого мета-содержимого в корне. Таким образом, другие инструменты Zip могут открыть пакет, но API Sysytem.IO.Packaging не может открыть все файлы Zip.

4
Henk Holterman

Это можно сделать, добавив ссылку на System.IO.Compression и System.IO.Compression.Filesystem.

Пример метода createZipFile () может выглядеть следующим образом:

public static void createZipFile(string inputfile, string outputfile, CompressionLevel compressionlevel)
        {
            try
            {
                using (ZipArchive za = ZipFile.Open(outputfile, ZipArchiveMode.Update))
                {
                    //using the same file name as entry name
                    za.CreateEntryFromFile(inputfile, inputfile);
                }
            }
            catch (ArgumentException)
            {
                Console.WriteLine("Invalid input/output file.");
                Environment.Exit(-1);
            }
}

где

  • inputfile = строка с именем файла, который будет сжат (для этого примера, вы должны добавить расширение)
  • outputfile = строка с именем Zip-файла назначения

Подробнее о классе ZipFile в MSDN

0
Juan Andrés Cid Molina

Это можно сделать всего одной строкой кода, используя только Dot Net. Ниже приведен пример кода, скопированного из MSDN

Шаг 1: Добавьте ссылку на System.IO.Compression.FileSystem

Шаг 2: следуйте приведенному ниже коду

        static void Main(string[] args)
        {
            string startPath = @"c:\example\start";
            string zipPath = @"c:\example\result.Zip";
            string extractPath = @"c:\example\extract";

            ZipFile.CreateFromDirectory(startPath, zipPath);

            ZipFile.ExtractToDirectory(zipPath, extractPath);
        }
0
Sujeewa

Вот некоторый код, который я написал после использования вышеупомянутых постов. Спасибо за вашу помощь. 

Этот код принимает список путей к файлам и создает из них файл Zip. 

public class Zip
{
    private string _filePath;
    public string FilePath { get { return _filePath; } }

    /// <summary>
    /// Zips a set of files
    /// </summary>
    /// <param name="filesToZip">A list of filepaths</param>
    /// <param name="sZipFileName">The file name of the new Zip (do not include the file extension, nor the full path - just the name)</param>
    /// <param name="deleteExistingZip">Whether you want to delete the existing Zip file</param>
    /// <remarks>
    /// Limitation - all files must be in the same location. 
    /// Limitation - must have read/write/edit access to folder where first file is located.
    /// Will throw exception if the Zip file already exists and you do not specify deleteExistingZip
    /// </remarks>
    public Zip(List<string> filesToZip, string sZipFileName, bool deleteExistingZip = true)
    {
        if (filesToZip.Count > 0)
        {
            if (File.Exists(filesToZip[0]))
            {

                // Get the first file in the list so we can get the root directory
                string strRootDirectory = Path.GetDirectoryName(filesToZip[0]);

                // Set up a temporary directory to save the files to (that we will eventually Zip up)
                DirectoryInfo dirTemp = Directory.CreateDirectory(strRootDirectory + "/" + DateTime.Now.ToString("yyyyMMddhhmmss"));

                // Copy all files to the temporary directory
                foreach (string strFilePath in filesToZip)
                {
                    if (!File.Exists(strFilePath))
                    {
                        throw new Exception(string.Format("File {0} does not exist", strFilePath));
                    }
                    string strDestinationFilePath = Path.Combine(dirTemp.FullName, Path.GetFileName(strFilePath));
                    File.Copy(strFilePath, strDestinationFilePath);
                }

                // Create the Zip file using the temporary directory
                if (!sZipFileName.EndsWith(".Zip")) { sZipFileName += ".Zip"; }
                string strZipPath = Path.Combine(strRootDirectory, sZipFileName);
                if (deleteExistingZip == true && File.Exists(strZipPath)) { File.Delete(strZipPath); }
                ZipFile.CreateFromDirectory(dirTemp.FullName, strZipPath, CompressionLevel.Fastest, false);

                // Delete the temporary directory
                dirTemp.Delete(true);

                _filePath = strZipPath;                    
            }
            else
            {
                throw new Exception(string.Format("File {0} does not exist", filesToZip[0]));
            }
        }
        else
        {
            throw new Exception("You must specify at least one file to Zip.");
        }
    }
}
0
Tony Dougherty