System.IO.Packaging provides storage support for multiple data objects in a single container. System.IO.Packaging uses the ZIP file format as its primary physical format for a Package. Other Package implementations might use an XML document, a database, or Web service.
The following contains a utility class I wrote to help zip a file or a folder into a ZIP archive. This is an asynchronous as is suitable for invoking from a Win Forms or a WPF Application when we need to display a progress bar. Here is the code:
using System;
using System.IO;
using System.IO.Packaging;
using System.Linq;
using System.Web;
using InvertedSoftware.Common.Events;
namespace InvertedSoftware.IO
{
public delegate void ZipEventHandler(object sender, ZipEventArgs e);
public delegate void ZipEndEventHandler(object sender, ZipEndEventArgs e);
/// <summary>
/// Utility class for Zipping and Unzipping files and folders into XPS Packages
/// This class uses XML Paper Specification and adds [Content_Types].xml to the package
/// </summary>
public class Packaging
{
#region Events
private delegate void AsyncStartPackageFolder(string folderName, string compressedFileName, bool overrideExisting);
private delegate void AsyncStartPackageFile(string fileName, string compressedFileName, bool overrideExisting);
/// <summary>
/// Add a folder along with its subfolders to a Package Asynchronously
/// </summary>
/// <param name="folderName">The folder to add</param>
/// <param name="compressedFileName">The package to create</param>
/// <param name="overrideExisting">Override exsisitng files</param>
public void PackageFolderAsync(string folderName, string compressedFileName, bool overrideExisting)
{
AsyncStartPackageFolder methodCall = new AsyncStartPackageFolder(StartPackageFolder);
methodCall.BeginInvoke(folderName,
compressedFileName,
overrideExisting,
new AsyncCallback(RunZipComplete),
compressedFileName);
}
/// <summary>
/// Compress a file into a ZIP archive as the container store Asynchronously
/// </summary>
/// <param name="fileName">The file to compress</param>
/// <param name="compressedFileName">The archive file</param>
/// <param name="overrideExisting">override existing file</param>
public void PackageFileAsync(string fileName, string compressedFileName, bool overrideExisting)
{
AsyncStartPackageFolder methodCall = new AsyncStartPackageFolder(StartPackageFile);
methodCall.BeginInvoke(fileName,
compressedFileName,
overrideExisting,
new AsyncCallback(RunZipComplete),
compressedFileName);
}
private void StartPackageFolder(string folderName, string compressedFileName, bool overrideExisting)
{
PackageFolder(folderName, compressedFileName, overrideExisting);
}
private void StartPackageFile(string fileName, string compressedFileName, bool overrideExisting)
{
PackageFile(fileName, compressedFileName, overrideExisting);
}
private void RunZipComplete(IAsyncResult result)
{
}
public event ZipEventHandler Zip;
protected virtual void OnZip(ZipEventArgs e)
{
if (Zip != null)
Zip(this, e);
}
public event ZipEndEventHandler ZipEnd;
protected virtual void OnZipEnd(ZipEndEventArgs e)
{
if (ZipEnd != null)
ZipEnd(this, e);
}
#endregion
private const string PackageRelationshipType = @"http://schemas.microsoft.com/opc/2006/sample/document";
private const string ResourceRelationshipType = @"http://schemas.microsoft.com/opc/2006/sample/required-resource";
/// <summary>
/// Add a folder along with its subfolders to a Package
/// </summary>
/// <param name="folderName">The folder to add</param>
/// <param name="compressedFileName">The package to create</param>
/// <param name="overrideExisting">Override exsisitng files</param>
/// <returns>ReturnResult</returns>
public ReturnResult PackageFolder(string folderName, string compressedFileName, bool overrideExisting)
{
if (folderName.EndsWith(@"\"))
folderName = folderName.Remove(folderName.Length - 1);
ReturnResult result = new ReturnResult();
if (!Directory.Exists(folderName))
{
result.Message = "Source folder doesnt exist in" + folderName;
return result;
}
if (!overrideExisting && File.Exists(compressedFileName))
{
result.Message = "Destination file " + compressedFileName + " cannot be overwritten";
return result;
}
try
{
using (Package package = Package.Open(compressedFileName, FileMode.Create))
{
var fileList = Directory.EnumerateFiles(folderName, "*", SearchOption.AllDirectories);
ZipEventArgs zipArgs = new ZipEventArgs() { TotalFiles = fileList.Count(), FileNumber = 0, BytesZipped = 0, TotalBytes = 0 };
foreach (string fileName in fileList)
{
zipArgs.FileNumber++;
if (Path.GetFileName(fileName).IndexOfAny(Path.GetInvalidFileNameChars()) > -1)
continue;
//The path in the package is all of the subfolders after folderName
string pathInPackage;
pathInPackage = Path.GetDirectoryName(fileName).Replace(folderName, string.Empty) + "/" + Path.GetFileName(fileName);
Uri partUriDocument = PackUriHelper.CreatePartUri(new Uri(pathInPackage, UriKind.Relative));
PackagePart packagePartDocument = package.CreatePart(partUriDocument, System.Net.Mime.MediaTypeNames.Text.Xml, CompressionOption.Maximum);
using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
fileStream.CopyTo(packagePartDocument.GetStream());
zipArgs.BytesZipped += fileStream.Length;
OnZip(zipArgs);
}
package.CreateRelationship(packagePartDocument.Uri, TargetMode.Internal, PackageRelationshipType);
}
}
}
catch (Exception e)
{
throw new Exception("Error zipping folder " + folderName, e);
}
OnZipEnd(new ZipEndEventArgs());
result.Success = true;
result.Message = "OK";
return result;
}
/// <summary>
/// Compress a file into a ZIP archive as the container store
/// </summary>
/// <param name="fileName">The file to compress</param>
/// <param name="compressedFileName">The archive file</param>
/// <param name="overrideExisting">override existing file</param>
/// <returns>ReturnResult</returns>
public ReturnResult PackageFile(string fileName, string compressedFileName, bool overrideExisting)
{
ReturnResult result = new ReturnResult();
if (!File.Exists(fileName))
{
result.Message = "Source file doesnt exist in" + fileName;
return result;
}
if (!overrideExisting && File.Exists(compressedFileName))
{
result.Message = "Destination file " + compressedFileName + " cannot be overwritten";
return result;
}
try
{
Uri partUriDocument = PackUriHelper.CreatePartUri(new Uri(Path.GetFileName(fileName), UriKind.Relative));
using (Package package = Package.Open(compressedFileName, FileMode.Create))
{
PackagePart packagePartDocument = package.CreatePart(partUriDocument, System.Net.Mime.MediaTypeNames.Text.Xml, CompressionOption.Maximum);
using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
ZipEventArgs zipArgs = new ZipEventArgs() { TotalFiles = 1, FileNumber = 1, TotalBytes = new FileInfo(fileName).Length, BytesZipped = 0 };
OnZip(zipArgs);
fileStream.CopyTo(packagePartDocument.GetStream());
zipArgs.BytesZipped += fileStream.Length;
OnZip(zipArgs);
}
package.CreateRelationship(packagePartDocument.Uri, TargetMode.Internal, PackageRelationshipType);
}
}
catch (Exception e)
{
throw new Exception("Error zipping file " + fileName, e);
}
OnZipEnd(new ZipEndEventArgs());
result.Success = true;
result.Message = "OK";
return result;
}
/// <summary>
/// Extract a container Zip. NOTE: container must be created as Open Packaging Conventions (OPC) specification.
/// Delete [Content_Types].xml when done
/// </summary>
/// <param name="folderName">The folder to extract the package to</param>
/// <param name="compressedFileName">The package file</param>
/// <param name="overrideExisting">override existing files</param>
/// <returns>ReturnResult</returns>
public ReturnResult UncompressFile(string folderName, string compressedFileName, bool overrideExisting)
{
return UncompressFile(folderName, compressedFileName, overrideExisting, true);
}
/// <summary>
/// Extract a container Zip. NOTE: container must be created as Open Packaging Conventions (OPC) specification
/// </summary>
/// <param name="folderName">The folder to extract the package to</param>
/// <param name="compressedFileName">The package file</param>
/// <param name="overrideExisting">override existing files</param>
/// <param name="removeDescFile">Delete [Content_Types].xml when done</param>
/// <returns>ReturnResult</returns>
public ReturnResult UncompressFile(string folderName, string compressedFileName, bool overrideExisting, bool removeDescFile)
{
ReturnResult result = new ReturnResult();
try
{
if (!File.Exists(compressedFileName))
{
result.Success = false;
result.Message = "Compressed File not found";
return result;
}
DirectoryInfo directoryInfo = new DirectoryInfo(folderName);
if (!directoryInfo.Exists)
directoryInfo.Create();
using (Package package = Package.Open(compressedFileName, FileMode.Open, FileAccess.Read))
{
PackagePart documentPart = null;
PackagePart resourcePart = null;
Uri uriDocumentTarget = null;
foreach (PackageRelationship relationship in package.GetRelationshipsByType(PackageRelationshipType))
{
uriDocumentTarget = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), relationship.TargetUri);
documentPart = package.GetPart(uriDocumentTarget);
ExtractPart(documentPart, folderName, overrideExisting);
}
if (documentPart != null)
{
Uri uriResourceTarget = null;
foreach (PackageRelationship relationship in documentPart.GetRelationshipsByType(ResourceRelationshipType))
{
uriResourceTarget = PackUriHelper.ResolvePartUri(documentPart.Uri, relationship.TargetUri);
resourcePart = package.GetPart(uriResourceTarget);
ExtractPart(resourcePart, folderName, overrideExisting);
}
}
}
if (removeDescFile && File.Exists(folderName + "\\[Content_Types].xml"))
File.Delete(folderName + "\\[Content_Types].xml");
}
catch (Exception e)
{
throw new Exception("Error unzipping file " + compressedFileName, e);
}
OnZipEnd(new ZipEndEventArgs());
result.Success = true;
result.Message = "OK";
return result;
}
private void ExtractPart(PackagePart packagePart, string targetDirectory, bool overrideExisting)
{
string stringPart = targetDirectory + HttpUtility.UrlDecode(packagePart.Uri.ToString()).Replace('\\', '/');
if (!Directory.Exists(Path.GetDirectoryName(stringPart)))
Directory.CreateDirectory(Path.GetDirectoryName(stringPart));
if (!overrideExisting && File.Exists(stringPart))
return;
using (FileStream fileStream = new FileStream(stringPart, FileMode.Create))
{
packagePart.GetStream().CopyTo(fileStream);
}
}
}
/// <summary>
/// This is the standard output of any generic function
/// </summary>
public struct ReturnResult
{
public string Message;
public bool Success;
public string Title;
}
}
using System;
namespace InvertedSoftware.Common.Events
{
/// <summary>
/// A Zip event
/// </summary>
public class ZipEventArgs : EventArgs
{
/// <summary>
/// Total bytes to zip
/// </summary>
public long TotalBytes { get; set; }
/// <summary>
/// Bytes zipped
/// </summary>
public long BytesZipped { get; set; }
/// <summary>
/// Total files to zip
/// </summary>
public int TotalFiles { get; set; }
/// <summary>
/// Fileld Zipped
/// </summary>
public int FileNumber { get; set; }
}
}
using System;
namespace InvertedSoftware.Common.Events
{
/// <summary>
/// Zip End event
/// </summary>
public class ZipEndEventArgs : EventArgs
{
}
}
Usage will look like this:
using System;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
InvertedSoftware.IO.Packaging packaging = new InvertedSoftware.IO.Packaging();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
packaging.Zip += new InvertedSoftware.IO.ZipEventHandler(packaging_Zip);
packaging.ZipEnd += new InvertedSoftware.IO.ZipEndEventHandler(packaging_ZipEnd);
packaging.PackageFolderAsync(@"C:\FrameworkTest\Source\", @"C:\FrameworkTest\Destination\myCompressedFile.zip", true);
}
void packaging_ZipEnd(object sender, InvertedSoftware.Common.Events.ZipEndEventArgs e)
{
packaging.UncompressFile(@"C:\FrameworkTest\Destination\myCompressedFolder", @"C:\FrameworkTest\Destination\myCompressedFile.zip", true);
}
void packaging_Zip(object sender, InvertedSoftware.Common.Events.ZipEventArgs e)
{
}
}
}
You might notice that the archive file contain an extra file named [Content_Types].xml this file is used by System.IO.Packaging to enumerate the file types in the package.
Posted
24 Jan 2011 2:11 PM
by
Gal Ratner