使用 DotNetZip 通过 ASP.NET MVC 下载 zip 文件
Downloading of zip file through ASP.NET MVC using DotNetZip
我在一个文件夹中创建了一个文本文件并将该文件夹压缩并保存@same 位置用于测试目的。我想在创建后直接在用户机器上下载该 zip 文件。我正在使用 dotnetzip 库并完成了以下操作:
Response.Clear();
Response.ContentType = "application/zip";
Response.AddHeader("content-disposition", "filename=" + "sample.zip");
using (ZipFile zip = new ZipFile())
{
zip.AddDirectory(Server.MapPath("~/Directories/hello"));
zip.Save(Server.MapPath("~/Directories/hello/sample.zip"));
}
有人可以建议如何在用户端下载 zip 文件吗?
您可以使用控制器的 File
方法来 return 一个文件,例如:
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");
}
}
如果zip文件不需要另外存储,则不需要将其写入服务器上的文件:
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");
}
}
创建一个 GET
-only 控制器操作 returns 一个 FileResult
,像这样:
[HttpGet]
public FileResult Download()
{
// Create file on disk
using (ZipFile zip = new ZipFile())
{
zip.AddDirectory(Server.MapPath("~/Directories/hello"));
//zip.Save(Response.OutputStream);
zip.Save(Server.MapPath("~/Directories/hello/sample.zip"));
}
// Read bytes from disk
byte[] fileBytes = System.IO.File.ReadAllBytes(
Server.MapPath("~/Directories/hello/sample.zip"));
string fileName = "sample.zip";
// Return bytes as stream for download
return File(fileBytes, "application/zip", fileName);
}
首先,考虑一种不在服务器磁盘上创建任何文件的方法。不好的做法。我建议创建一个文件并将其压缩到内存中。希望您会发现我下面的示例很有用。
/// <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");
}
以上代码注释:
- 传递
MemoryStream
实例需要检查它是否打开、有效等。我省略了它们。我宁愿传递文件内容的字节数组而不是 MemoryStream
实例以使代码更健壮,但对于这个例子来说太多了。
- 它没有显示如何在内存中创建所需的上下文(您的文件)。我会参考 MemoryStream class 的说明。
对于那些只想从 App_Data 文件夹中 return 现有 Zip 文件(只需将你的 zip 文件转储到那里)的用户,在 Home 控制器中创建此操作方法:
public FileResult DownLoad(string filename)
{
var content = XFile.GetFile(filename);
return File(content, System.Net.Mime.MediaTypeNames.Application.Zip, filename);
}
获取文件是一种扩展方法:
public static byte[] GetFile(string name)
{
string path = AppDomain.CurrentDomain.GetData("DataDirectory").ToString();
string filenanme = path + "/" + name;
byte[] bytes = File.ReadAllBytes(filenanme);
return bytes;
}
家庭控制器索引视图如下所示:
@model List<FileInfo>
<table class="table">
<tr>
<th>
@Html.DisplayName("File Name")
</th>
<th>
@Html.DisplayName("Last Write Time")
</th>
<th>
@Html.DisplayName("Length (mb)")
</th>
<th></th>
</tr>
@foreach (var item in Model)
{
<tr>
<td>
@Html.ActionLink("DownLoad","DownLoad",new {filename=item.Name})
</td>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.LastWriteTime)
</td>
<td>
@Html.DisplayFor(modelItem => item.Length)
</td>
</tr>
}
</table>
主索引文件操作方法:
public ActionResult Index()
{
var names = XFile.GetFileInformation();
return View(names);
}
其中 GetFileInformation 是扩展方法:
public static List<FileInfo> GetFileInformation()
{
string path = AppDomain.CurrentDomain.GetData("DataDirectory").ToString();
var dirInfo = new DirectoryInfo(path);
return dirInfo.EnumerateFiles().ToList();
}
只是对 Klaus 解决方案的修复:(因为我无法添加评论,所以我必须添加另一个答案!)
这个解决方案很好,但对我来说它给出了损坏的 zip 文件,我意识到这是因为 return 在完成 zip 对象之前所以它没有关闭 zip 并导致 zip 损坏。
所以要修复我们只需要在使用 zip 块后移动 return 行就可以了。
最终结果是:
/// <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");
}
我在一个文件夹中创建了一个文本文件并将该文件夹压缩并保存@same 位置用于测试目的。我想在创建后直接在用户机器上下载该 zip 文件。我正在使用 dotnetzip 库并完成了以下操作:
Response.Clear();
Response.ContentType = "application/zip";
Response.AddHeader("content-disposition", "filename=" + "sample.zip");
using (ZipFile zip = new ZipFile())
{
zip.AddDirectory(Server.MapPath("~/Directories/hello"));
zip.Save(Server.MapPath("~/Directories/hello/sample.zip"));
}
有人可以建议如何在用户端下载 zip 文件吗?
您可以使用控制器的 File
方法来 return 一个文件,例如:
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");
}
}
如果zip文件不需要另外存储,则不需要将其写入服务器上的文件:
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");
}
}
创建一个 GET
-only 控制器操作 returns 一个 FileResult
,像这样:
[HttpGet]
public FileResult Download()
{
// Create file on disk
using (ZipFile zip = new ZipFile())
{
zip.AddDirectory(Server.MapPath("~/Directories/hello"));
//zip.Save(Response.OutputStream);
zip.Save(Server.MapPath("~/Directories/hello/sample.zip"));
}
// Read bytes from disk
byte[] fileBytes = System.IO.File.ReadAllBytes(
Server.MapPath("~/Directories/hello/sample.zip"));
string fileName = "sample.zip";
// Return bytes as stream for download
return File(fileBytes, "application/zip", fileName);
}
首先,考虑一种不在服务器磁盘上创建任何文件的方法。不好的做法。我建议创建一个文件并将其压缩到内存中。希望您会发现我下面的示例很有用。
/// <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");
}
以上代码注释:
- 传递
MemoryStream
实例需要检查它是否打开、有效等。我省略了它们。我宁愿传递文件内容的字节数组而不是MemoryStream
实例以使代码更健壮,但对于这个例子来说太多了。 - 它没有显示如何在内存中创建所需的上下文(您的文件)。我会参考 MemoryStream class 的说明。
对于那些只想从 App_Data 文件夹中 return 现有 Zip 文件(只需将你的 zip 文件转储到那里)的用户,在 Home 控制器中创建此操作方法:
public FileResult DownLoad(string filename)
{
var content = XFile.GetFile(filename);
return File(content, System.Net.Mime.MediaTypeNames.Application.Zip, filename);
}
获取文件是一种扩展方法:
public static byte[] GetFile(string name)
{
string path = AppDomain.CurrentDomain.GetData("DataDirectory").ToString();
string filenanme = path + "/" + name;
byte[] bytes = File.ReadAllBytes(filenanme);
return bytes;
}
家庭控制器索引视图如下所示:
@model List<FileInfo>
<table class="table">
<tr>
<th>
@Html.DisplayName("File Name")
</th>
<th>
@Html.DisplayName("Last Write Time")
</th>
<th>
@Html.DisplayName("Length (mb)")
</th>
<th></th>
</tr>
@foreach (var item in Model)
{
<tr>
<td>
@Html.ActionLink("DownLoad","DownLoad",new {filename=item.Name})
</td>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.LastWriteTime)
</td>
<td>
@Html.DisplayFor(modelItem => item.Length)
</td>
</tr>
}
</table>
主索引文件操作方法:
public ActionResult Index()
{
var names = XFile.GetFileInformation();
return View(names);
}
其中 GetFileInformation 是扩展方法:
public static List<FileInfo> GetFileInformation()
{
string path = AppDomain.CurrentDomain.GetData("DataDirectory").ToString();
var dirInfo = new DirectoryInfo(path);
return dirInfo.EnumerateFiles().ToList();
}
只是对 Klaus 解决方案的修复:(因为我无法添加评论,所以我必须添加另一个答案!)
这个解决方案很好,但对我来说它给出了损坏的 zip 文件,我意识到这是因为 return 在完成 zip 对象之前所以它没有关闭 zip 并导致 zip 损坏。
所以要修复我们只需要在使用 zip 块后移动 return 行就可以了。 最终结果是:
/// <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");
}