web-dev-qa-db-ja.com

プログラムで通常のZipファイルを作成する

C#で単一のファイルを圧縮する方法に関する多くのチュートリアルを見てきました。ただし、複数のファイルから通常の* .Zipファイルを作成できる必要があります。これを実行できる.NETには何かありますか?あなたは何を提案しますか(私は厳しい規則の下にあり、他のライブラリを使用できないことに留意してください)

ありがとうございました

36
anon271334

編集:.Net 4.5以降を使用している場合、これは フレームワークへの組み込み です

以前のバージョンまたはより詳細な制御のために、概説されているようにWindowsのシェル関数を使用できます ここではGerald Gibson JrのCodeProject

以下の記事のテキストを書かれたとおりにコピーしました(元のライセンス: パブリックドメイン

Windows Shell APIとCでZipファイルを圧縮する

enter image description here

前書き

これは、Zipファイルの解凍について書いた記事のフォローアップ記事です。このコードを使用すると、C#のWindowsシェルAPIを使用してZipファイルを圧縮でき、上記の[コピーの進行状況]ウィンドウを表示する必要はありません。通常、シェルAPIを使用してZipファイルを圧縮すると、Windowsに表示しないようにオプションを設定した場合でも、コピー進行​​状況ウィンドウが表示されます。これを回避するには、シェルAPIコードを別の実行可能ファイルに移動してから、.NETプロセスクラスを使用してその実行可能ファイルを起動し、プロセスウィンドウのスタイルを「非表示」に設定します。

バックグラウンド

Zipファイルを圧縮する必要があり、多くの無料の圧縮ライブラリに付属しているものよりも優れたZipが必要でしたか?つまりフォルダーとサブフォルダー、およびファイルを圧縮する必要がありました。 Windows Zippingは、個々のファイルを圧縮するだけではありません。必要なのは、プログラムでWindowsにこれらのZipファイルをサイレントに圧縮させる方法です。もちろん、市販のZipコンポーネントの1つに300ドルを費やすこともできますが、フォルダー階層を圧縮することだけが必要な場合、無料で勝つことは困難です。

コードを使用する

次のコードは、WindowsシェルAPIを使用してZipファイルを圧縮する方法を示しています。最初に空のZipファイルを作成します。これを行うには、適切に構築されたバイト配列を作成し、その配列を「.Zip」拡張子を持つファイルとして保存します。配列に入れるバイトをどのように知りましたか?さて、Windowsを使用して、単一のファイルを圧縮したZipファイルを作成しました。次に、WindowsでZipを開き、圧縮ファイルを削除しました。空のZipが残った。次に、16進エディター(Visual Studio)で空のZipファイルを開き、16進バイト値を調べてWindows Calcで10進数に変換し、それらの10進値をバイト配列コードにコピーしました。ソースフォルダーは、圧縮するフォルダーを指します。宛先フォルダーは、作成したばかりの空の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を圧縮する方法を示しています。

以下のコードは、圧縮中にUIがないようにコンソールアプリケーションを起動するために使用されるコードを含むボタンクリックイベントハンドラーを示しています。

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");
}
18
stuartd

この質問に出くわした他の人のために、これに関する単なる更新。

.NET 4.5以降では、System.IO.Compressionを使用してディレクトリをZipファイルに圧縮できます。 System.IO.Compression.FileSystemはデフォルトでは参照されないため、参照として追加する必要があります。その後、あなたは書くことができます:

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

唯一の潜在的な問題は、このアセンブリがWindowsストアアプリで利用できないことです。

62
Danny

.NET 4.5から利用可能な ZipArchive クラス(System.IO.Compression.ZipArchive)を使用できるようになりました

例:PDFファイルのZipの生成

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);
            }
        }
    }
}
29
andersh

私の2セント:

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

したがって、Zipファイルはfiles/dirsから直接作成できます。

21
Guy L

考慮すべきいくつかのリソースを次に示します。 。NETでのZipアーカイブの作成(SharpZipLibなどの外部ライブラリなし)

System.IO.Packagingでストリームを圧縮

私の推奨および好みは、system.io.packackingを使用することです。これにより、依存関係が抑えられます(フレームワークのみ)。 Jgallowayの投稿(最初のリファレンス)は、Zipファイルに2つのファイルを追加する良い例です。はい、それはより冗長ですが、ファサードを簡単に作成できます(ある程度、AddFileToZipはそれを行います)。

HTH

20
Jake

あなたは 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ツールはパッケージを開くことができますが、Sysytem.IO.Packaging APIはすべての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);
            }
}

where

  • inputfile =圧縮するファイル名を含む文字列(この例では、拡張子を追加する必要があります)
  • outputfile =宛先のZipファイル名を含む文字列

MSDNのZipFileクラスの詳細

上記の投稿を使用して作成したコードを次に示します。ご助力いただきありがとうございます。

このコードは、ファイルパスのリストを受け入れ、それらから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.");
        }
    }
}
1
Tony Dougherty

これは、ドットネットだけを使用して1行のコードで実行できます。以下は [〜#〜] 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