PROWAREtech
ASP.NET Core: ZipFile Stream Options in .NET 8 and Later
As of now, the ZipFile
class can extract to a directory from a Stream
class and write to a Stream
from a directory. Before, when working with ZipFile
, one had to "override" the Dispose()
method in the FileStream
class to delete the zip file after sending it to the browser (see article on this topic). Using the MemoryStream
class instead of the FileStream
class — while convenient — is inefficient and care must be taken when working with large amounts of data and/or large numbers of users.
Example Code
Create a .NET 8 Model-View-Controller (MVC) application project. Make the following additions and changes to the project.
Modify Index.cshtml
Modify the Index.cshtml file to be this:
@model IEnumerable<string>
@{
ViewData["Title"] = "Home Page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
@* NOTE: NEWLY ADDED CODE *@
<div style="margin:10rem auto;width:400px;">
<hr />
<p><a href="/?downloadzip=true">download a zip file created with a stream</a></p>
<hr />
<form method="post" enctype="multipart/form-data">
<table>
<tr>
<td><input type="file" accept="application/x-zip-compressed" name="zip-file" required /></td>
<td><button type="submit">view zip file contents</button></td>
</tr>
</table>
</form>
<hr />
</div>
@if (Model != null)
{
<h5>Contents</h5>
<div>
@foreach (var s in Model)
{
<div>@s</div>
}
</div>
}
@* NOTE: END OF NEWLY ADDED CODE *@
Modify HomeController.cs
Modify the HomeController.cs file to be this:
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using System.IO.Compression; // NOTE: this line is newly added
using ZipFileExample.Models;
namespace ZipFileExample.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IWebHostEnvironment _env; // NOTE: this line is newly added
public HomeController(ILogger<HomeController> logger, IWebHostEnvironment env) // NOTE: this line is modified
{
_logger = logger;
_env = env; // NOTE: this line is newly added
}
// NOTE: following block of code/method is modified
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Index(bool downloadzip = false)
{
if (downloadzip)
{
using (var ms = new MemoryStream())
{
ZipFile.CreateFromDirectory(_env.WebRootPath, ms);
return File(ms.ToArray(), "application/x-zip-compressed", "wwwroot.zip");
}
}
return View();
}
// NOTE: following block of code/method is newly added
[HttpPost]
[DisableRequestSizeLimit] // NOTE: in production code, set a reasonable limit
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Index(IFormCollection form)
{
var model = new List<string>();
if (Request.Form.Files.Count > 0 && Request.Form.Files[0].ContentType.ToLower() == "application/x-zip-compressed")
{
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(tempPath);
using (var zipStream = Request.Form.Files[0].OpenReadStream())
{
ZipFile.ExtractToDirectory(zipStream, tempPath);
foreach(var item in Directory.EnumerateFileSystemEntries(tempPath))
model.Add(Path.GetFileName(item));
}
Directory.Delete(tempPath, true);
}
return View(model);
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
Hit Ctrl+F5 to run the application and it should look like this:
Now explore the new options on this page!