Working With Zip Files In ASP.NET MVC

Since ASP.NET 4.5, zipping and unzipping files can be done with classes from within the .NET framework. Despite that, nearly all examples that illustrate working with zip files in ASP.NET applications feature third party open source compression libraries. This short article attempts to correct that by demonstrating the use of the System.IO.Compression classes to unpack an uploaded zip file, and to create one for download in an ASP.NET MVC application.

Unpacking an uploaded zip file

To begin, you need to add a reference to System.IO.Compression to your project. Having done that, here is an upload form for the zip file:

@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    <input type="file" name="zip"  /> 
    <div>
        <button class="btn btn-default">Submit</button>
    </div>
}

Note that the form's method is specified as POST and that the enctype attribute is set with a value of multipart/form-data - both prerequisites for being able to upload files. The form is posted to the following controller action:

[HttpPost]
public ActionResult Index(HttpPostedFileBase zip)
{
    var uploads = Server.MapPath("~/uploads");
    using (ZipArchive archive = new ZipArchive(zip.InputStream))
    {
        foreach (ZipArchiveEntry entry in archive.Entries)
        {
            if (!string.IsNullOrEmpty(Path.GetExtension(entry.FullName))) //make sure it's not a folder
            {
                entry.ExtractToFile(Path.Combine(uploads, entry.FullName));
            }
            else
            {
                Directory.CreateDirectory(Path.Combine(uploads, entry.FullName));
            }
        }
    }
    ViewBag.Files = Directory.EnumerateFiles(uploads);
    return View();
}

The action's parameter is an HttpPostedFileBase named after the name attribute of the file upload control in the form. The code creates an instance of the ZipArchive class from the content of the uploaded file and then iterates the contents of the archive. If the name of an entry has a file extension, it is a file. If not, it is a folder. The code will save the extracted file or create a folder accordingly.

Creating a Zip file for download

The simple example that follows illustrates the use of the static ZipFile.CreateFromDirectory method which, despite the fact that it is in the System.IO.Compression namespace , actually resides in the System.IO.Compression.FileSystem assembly, so you need to add a reference to that in your controller. The user is presented with a list of checkboxes representing a selection of files to choose from. Submitting the form will result in just those files being packaged up into one zip file and downloaded.

The list of files is passed to the view via ViewBag:

public ActionResult Index()
{
    ViewBag.Files = Directory.EnumerateFiles(Server.MapPath("~/pdfs"));
    return View();
}

The files are listed within a form with a set of checkboxes:

<h2>Select downloads</h2>
@using(Html.BeginForm("Download", "Home"))
{
    foreach(string file in ViewBag.Files)
    {
        <input type="checkbox" name="files" value="@file" /> @:&nbsp;  
             @Path.GetFileNameWithoutExtension(file) <br />
    }
    <div>
        <button class="btn btn-default">Submit</button>
    </div>
}

The form posts to an action called Download which consists of the following code:

[HttpPost]
public FileResult Download(List<string> files)
{
    var archive = Server.MapPath("~/archive.zip");
    var temp = Server.MapPath("~/temp");

    // clear any existing archive
    if (System.IO.File.Exists(archive))
    {
        System.IO.File.Delete(archive);
    }
    // empty the temp folder
    Directory.EnumerateFiles(temp).ToList().ForEach(f => System.IO.File.Delete(f));

    // copy the selected files to the temp folder
    files.ForEach(f => System.IO.File.Copy(f, Path.Combine(temp, Path.GetFileName(f))));

    // create a new archive
    ZipFile.CreateFromDirectory(temp, archive);

    return File(archive, "application/zip", "archive.zip");
}

The user selection is captured in the files parameter. The code checks to see if a file called archive.zip exists from previous operations and it it does, it is deleted. Then a folder called temp is cleared of any existing files. Next,the selected files are copied from their source directory to the temp folder. The ZipFile.CreateFromDirectory method generates a zip file from the temp directory contents and saves it as archive.zip. Finally, it is written to the Response.

Both of these examples are simplified to illustrate the main classes and methods required to perform the tasks as clearly as possible. When handling the upload, you should add validation to check that the uploaded file is in fact a zip file, for example. Also, the zip file that gets created is placed into a hardcoded folder. If you have a busy site where multiple users are downloading zip files at the same time, you may want to use a Guid as a folder name instead to avoid the possibility of one user's download overwriting another.