在ASP.NET下载多个文件

这篇简短的文章探讨了令人困惑的问题,即在尝试从ASP.NET应用程序向客户端发送多个文件时,很多人似乎遇到的令人困惑的问题,并提供解决方案。

每一个经常在论坛上弹出一个问题,询问为什么他们的多个文件下载代码只发送第一个文件。通常,代码由循环组成迭代文件集合并尝试使用response.transmitfile或mvc中的fileResult以将每个文件分配给客户端。这似乎没有工作的原因是因为它基本上是不可能的。出于安全原因,不支持这一点。启用这种情况的潜在利用将被称为a驱动下载除了请求的文件之外,恶意网站管理员还可以向客户端发送整个恶意软件。

解决方法是将文件压缩为一个存档文件,然后下载。有相当多的例子,展示了如何使用各种第三方ZIP库来执行此操作。这主要是因为在.NET框架中没有专门为此设计,直到介绍了一些新的东西system.i.com in .NET 4.5, specifically the ZipFile class that enables you to create and work with .zip files.

The simple examples that follow illustrates the use of the ZipFile.CreateFromDirectory method in ASP.NET MVC and Web Forms. In both cases, 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.

ASP.NET MVC.

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

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

文件列在具有一组复选框的表单中:

<h2>Select downloadsh2>
@using(Html.BeginForm("Download", "Home"))
{
    foreach(string file in ViewBag.Files)
    {
        <input type="checkbox" name="files" value="@file" /> @:   
             @Path.GetFileNameWithoutExtension(file) <br />
    }
    <div>
        <button class="btn btn-default">Submitbutton>
    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 临时  = 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 code above relies on two additional using directives in the controller class:

using System.IO;
using system.i.com;

The user selection is captured in the files parameter. The code checks to see if a file called archive.zip.存在于以前的操作状态,它确实如此,它被删除了。然后一个文件夹临时 被清除了任何现有文件。接下来,将所选文件从其源目录复制到临时 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.

网络形态

此解决方案具有一个名为一页download.aspx.. The aspx file contains markup for a CheckBoxList and a Button control:

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <asp:CheckBoxList ID="CheckBoxList1" runat="server" OnDataBound="CheckBoxList1_DataBound" />
    <asp:Button ID="Button1" runat="server" Text="Submit" />
asp:Content>

The Page_Load method in the code behind file is very similar to the Action method in the MVC example. If the page hasn't been posted back, the list of files is obtained and bound to the CheckBoxList.

public partial class Download : Page
{
        
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            CheckBoxList1.DataSource = Directory.GetFiles(Server.MapPath("~/PDFs"));
            CheckBoxList1.DataBind();
        }
        else
        {
            var files = CheckBoxList1.Items.Cast<ListItem>()
                                                    .Where(li => li.Selected).Select(li => li.Value)
                                                    .ToList();
            var archive = Server.MapPath("~/archive.zip");
            var 临时  = 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);
            Response.ContentType = "application/zip";
            Response.AddHeader("Content-Disposition", "attachment; filename=archive.zip");
            Response.TransmitFile(archive);
        }
    }

    protected void CheckBoxList1_DataBound(object sender, EventArgs e)
    {
        foreach (ListItem item in CheckBoxList1.Items)
        {
            item.Text = Path.GetFileName(item.Text);
        }
    }
}

I have used the DataBound event of the CheckBoxList to format the Text property of each item to remove the full path to the file. If the form has been posted back, the selected items are stored in a List and then the very same code is used to clear previous files, copy the selected ones to the temp folder and then generate the zip archive. Finally, the content type and disposition of the Response are set appropriately and the archive is sent to the client.

概括

这篇简短的文章讨论了未启用多个文件下载的原因,并仅使用.NET Framework代码呈现了解决方法,以便在ASP.NET MVC和Web表单方案中提供解决方案。