15

我有一个下载动态生成文件的控制器操作:

    public ActionResult DownloadFile()
    {
        var obj = new MyClass { MyString = "Hello", MyBool = true };
        var ser = new XmlSerializer(typeof(MyClass));
        var stream = new MemoryStream();
        ser.Serialize(stream, obj);
        stream.Position = 0;

        Response.Clear();
        Response.AddHeader("Content-Disposition", "attachment; filename=myfile.xml");
        Response.ContentType = "application/xml";

        // Write all my data
        stream.WriteTo(Response.OutputStream);
        Response.End();

        return Content("Downloaded");
    }

仅供参考:

    public class MyClass
    {
        public string MyString { get; set; }
        public int MyInt { get; set; }
    }

这是有效的,并且文件 (myfile.xml) 已下载。
但是,“已下载”消息不会发送到浏览器。

同样,如果我替换return Content("Downloaded");
为,return Redirect("www.something.com");
则浏览器在文件下载之前被重定向。

作为一个序言,用户旅程是:

  • 用户在以前的视图中填写表格
  • 表格已提交
  • 生成并下载 XML
  • 用户被重定向/显示“已下载”视图(因此按 F5 不会重新发布表单)
4

3 回答 3

11

正如罗斯所说,您只能对 HTTP 请求返回一个响应。在这种情况下我要做的是:

  1. 将请求发送到服务器
  2. 服务器生成文件并将其存储在一些服务器端数据结构中(缓存、用户会话、临时数据)
  3. 服务器返回一个RedirectToAction()(POST、REDIRECT、GET 模式)
  4. 重定向的操作返回一个带有一些 javascript 的视图
  5. window.location.href通过将属性设置为将文件发送回浏览器的特殊下载操作来触发预生成文件的下载
于 2012-10-31T16:29:25.387 回答
9

每个 HTTP 请求只能有一个响应 - 您试图潜入两个(文件和页面)。

通常,当您发送“Content-Disposition:附件”HTTP 标头时,浏览器将停留在当前页面并弹出文件保存对话框(或自动将文件保存在下载中)。

如果您想阻止重新提交表单,您将不得不更改策略。我建议使用一些 javascript 来禁用表单的提交按钮并在 div 叠加层中显示“已完成”消息?

于 2012-10-25T14:45:34.263 回答
6

这是下载文件后我重定向的方式。主要逻辑是等待重定向,直到文件下载。为此,计算服务器端响应并使用服务器端响应时间 + 一些偏移量延迟重定向。

服务器端控制器代码:

[HttpPost]
public ActionResult GetTemplate()
    {
        return Json(new {Url = Url.Action("ReturnTemplate") });
    }

[HttpGet]
public ActionResult ReturnTemplate()
    {
        FileResult fileResult = // your file path ;
        return fileResult;
    }

客户端代码:

<div id="btnGen" align="right"><button class="main-button"     id="generateTemplate" type="Submit"></div>

Javascript:

$("#generateTemplate").click(function () {
    var startTime = (new Date()).getTime(), endTime;

        $.ajax({
            url: '@Url.Action("GetTemplate", "Controller")',
            type: 'POST',
            traditional: true,
            dataType: "json",
            contentType: "application/json",
            cache: false,
            data: JSON.stringify(),
            success: function (result) {
                endTime = (new Date()).getTime();
                var serverResponseTime = endTime - startTime + 500;
                setInterval(function () { Back() }, serverResponseTime);
                window.location = result.Url;
            }
        });
});

function Back() {
    window.location = '@Url.Action("Index","Controller")';
}
于 2015-09-24T18:40:14.043 回答