Downloading of zip file through ASP.NET MVC using DotNetZip
just a fix to Klaus solution: (as I can not add comment I have to add another answer!)
The solution is great but for me it gave corrupted zip file and I realized that it is because of return is before finalizing zip object so it did not close zip and result in a corrupted zip.
so to fix we need to just move return line after using zip block so it works. the final result is :
/// <summary>
/// Zip a file stream
/// </summary>
/// <param name="originalFileStream"> MemoryStream with original file </param>
/// <param name="fileName"> Name of the file in the ZIP container </param>
/// <returns> Return byte array of zipped file </returns>
private byte[] GetZippedFiles(MemoryStream originalFileStream, string fileName)
{
using (MemoryStream zipStream = new MemoryStream())
{
using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
{
var zipEntry = zip.CreateEntry(fileName);
using (var writer = new StreamWriter(zipEntry.Open()))
{
originalFileStream.WriteTo(writer.BaseStream);
}
}
return zipStream.ToArray();
}
}
/// <summary>
/// Download zipped file
/// </summary>
[HttpGet]
public FileContentResult Download()
{
var zippedFile = GetZippedFiles(/* your stream of original file */, "hello");
return File(zippedFile, // We could use just Stream, but the compiler gets a warning: "ObjectDisposedException: Cannot access a closed Stream" then.
"application/zip",
"sample.zip");
}
First of all, consider a way without creating any files on the server's disk. Bad practise. I'd recommend creating a file and zipping it in memory instead. Hope, you'll find my example below useful.
/// <summary>
/// Zip a file stream
/// </summary>
/// <param name="originalFileStream"> MemoryStream with original file </param>
/// <param name="fileName"> Name of the file in the ZIP container </param>
/// <returns> Return byte array of zipped file </returns>
private byte[] GetZippedFiles(MemoryStream originalFileStream, string fileName)
{
using (MemoryStream zipStream = new MemoryStream())
{
using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
{
var zipEntry = zip.CreateEntry(fileName);
using (var writer = new StreamWriter(zipEntry.Open()))
{
originalFileStream.WriteTo(writer.BaseStream);
}
return zipStream.ToArray();
}
}
}
/// <summary>
/// Download zipped file
/// </summary>
[HttpGet]
public FileContentResult Download()
{
var zippedFile = GetZippedFiles(/* your stream of original file */, "hello");
return File(zippedFile, // We could use just Stream, but the compiler gets a warning: "ObjectDisposedException: Cannot access a closed Stream" then.
"application/zip",
"sample.zip");
}
Notes to the code above:
- Passing a
MemoryStream
instance requires checks that it's open, valid and etc. I omitted them. I'd rather passed a byte array of the file content instead of aMemoryStream
instance to make the code more robust, but it'd be too much for this example. - It doesn't show how to create a required context (your file) in memory. I'd refer to MemoryStream class for instructions.
You may use the controller's File
method to return a file, like:
public ActionResult Download()
{
using (ZipFile zip = new ZipFile())
{
zip.AddDirectory(Server.MapPath("~/Directories/hello"));
zip.Save(Server.MapPath("~/Directories/hello/sample.zip"));
return File(Server.MapPath("~/Directories/hello/sample.zip"),
"application/zip", "sample.zip");
}
}
If the zip file is not required otherwise to be stored, it is unnecessary to write it into a file on the server:
public ActionResult Download()
{
using (ZipFile zip = new ZipFile())
{
zip.AddDirectory(Server.MapPath("~/Directories/hello"));
MemoryStream output = new MemoryStream();
zip.Save(output);
return File(output.ToArray(), "application/zip", "sample.zip");
}
}