【问题标题】:Stream file using ASP.NET MVC FileContentResult in a browser with a name?在具有名称的浏览器中使用 ASP.NET MVC FileContentResult 流文件?
【发布时间】:2010-07-08 18:16:14
【问题描述】:

有没有办法在浏览器中使用特定名称的 ASP.NET MVC FileContentResult 流式传输文件?

我注意到您可以使用 FileDialog(打开/保存),也可以在浏览器窗口中流式传输文件,但是当您尝试保存文件时它将使用 ActionName。

我有以下场景:

byte[] contents = DocumentServiceInstance.CreateDocument(orderId, EPrintTypes.Quote);
result = File(contents, "application/pdf", String.Format("Quote{0}.pdf", orderId));

当我使用它时,我可以流式传输字节,但会为用户提供一个打开/保存文件对话框。我想在浏览器窗口中实际流式传输此文件。

如果我只使用 FilePathResult,它会在浏览器窗口中显示文件,但是当我单击“保存”按钮将文件保存为 PDF 时,它会显示操作名称作为文件的名称。

有人遇到过吗?

【问题讨论】:

    标签: asp.net-mvc fileresult filecontentresult


    【解决方案1】:
    public ActionResult Index()
    {
        byte[] contents = FetchPdfBytes();
        return File(contents, "application/pdf", "test.pdf");
    }
    

    要在浏览器中打开 PDF,您需要设置 Content-Disposition 标头:

    public ActionResult Index()
    {
        byte[] contents = FetchPdfBytes();
        Response.AddHeader("Content-Disposition", "inline; filename=test.pdf");
        return File(contents, "application/pdf");
    }
    

    【讨论】:

    • 您好 Darin,这将打开一个打开/保存对话框,就像我提到的那样。我希望它在浏览器中打开一个文件。
    • 我们可以有一个通用值而不是 "application/pdf" 吗?就像我不确定文件的类型一样。首先是上传它的用户
    • 我们使用了这种方法,它会导致 MVC3 向浏览器发送 2 个 Content-Disposition 标头,从而导致 Chrome 和 Firefox 不显示该文件。 stackoverflow.com/q/8616691/304832
    • @AnilSoman,调用返回 AJAX 文件的控制器操作是没有意义的。如果你使用 AJAX,你永远不会得到任何文件打开框。
    • @AnilSoman,抱歉,AJAX 不可能。您可以使用一个普通的图像按钮来提交表单,而无需任何 AJAX 调用。
    【解决方案2】:

    其实,最简单的方法就是如下...

    byte[] content = your_byte[];
    
    FileContentResult result = new FileContentResult(content, "application/octet-stream") 
    {
      FileDownloadName = "your_file_name"
    };
    
    return result;
    

    【讨论】:

    • 如果原始问题不包含字节数组,我会同意。
    • 如果你像我一样有文件流,这不起作用,除非你想将流读入数组。
    • @SteveHiner 原帖特意把byte[]称为要渲染的数据。
    【解决方案3】:

    这可能对其他面临此问题的人有所帮助。我终于想出了一个解决办法。事实证明,即使我们使用“content-disposition”的内联并指定文件名,浏览器仍然不使用文件名。相反,浏览器会尝试根据路径/URL 解释文件名。

    您可以在此 URL 上进一步阅读: Securly download file inside browser with correct filename

    这给了我一个想法,我刚刚创建了我的 URL 路由,它将转换 URL 并以我想要提供的文件的名称结束它。所以例如我最初的控制器调用只包括传递正在打印的订单的订单 ID。我希望文件名的格式为 Order{0}.pdf,其中 {0} 是订单 ID。同样,对于引号,我想要 Quote{0}.pdf。

    在我的控制器中,我继续添加了一个附加参数来接受文件名。我在 URL.Action 方法中将文件名作为参数传递。

    然后我创建了一个新路由,将该 URL 映射到以下格式: http://localhost/ShoppingCart/PrintQuote/1054/Quote1054.pdf

    routes.MapRoute("", "{controller}/{action}/{orderId}/{fileName}", new { controller = "ShoppingCart", action = "PrintQuote" } , new string[] { "x.x.x.Controllers" } );

    这几乎解决了我的问题。希望这对某人有帮助!

    欢呼, 阿努普

    【讨论】:

    • 一个黑客,但一个非常有效的黑客!谢谢!
    • 不幸的是,IE 11 仍然需要它。Chrome 和 Firefox 不需要。
    • Brillant 为我工作......但我发现了一个问题,确保任何静态文件处理程序在请求到达托管代码之前都不会拦截它,这是我的另一个障碍。即默认情况下,对任何 *.jpg 的请求都不会靠近托管代码,因此甚至不会检查路由,额外的 here
    【解决方案4】:

    以前的答案是正确的:添加行...

    Response.AddHeader("Content-Disposition", "inline; filename=[filename]");
    

    ...将导致多个 Content-Disposition 标头向下发送到浏览器。如果您为标题提供文件名,则会发生这种情况 b/c FileContentResult 在内部应用标题。另一种非常简单的解决方案是简单地创建FileContentResult 的子类并覆盖其ExecuteResult() 方法。这是一个实例化System.Net.Mime.ContentDisposition 类的实例(与内部FileContentResult 实现中使用的对象相同)并将其传递给新类的示例:

    public class FileContentResultWithContentDisposition : FileContentResult
    {
        private const string ContentDispositionHeaderName = "Content-Disposition";
    
        public FileContentResultWithContentDisposition(byte[] fileContents, string contentType, ContentDisposition contentDisposition)
            : base(fileContents, contentType)
        {
            // check for null or invalid ctor arguments
            ContentDisposition = contentDisposition;
        }
    
        public ContentDisposition ContentDisposition { get; private set; }
    
        public override void ExecuteResult(ControllerContext context)
        {
            // check for null or invalid method argument
            ContentDisposition.FileName = ContentDisposition.FileName ?? FileDownloadName;
            var response = context.HttpContext.Response;
            response.ContentType = ContentType;
            response.AddHeader(ContentDispositionHeaderName, ContentDisposition.ToString());
            WriteFile(response);
        }
    }
    

    在您的Controller 或基础Controller 中,您可以编写一个简单的帮助程序来实例化FileContentResultWithContentDisposition,然后从您的操作方法中调用它,如下所示:

    protected virtual FileContentResult File(byte[] fileContents, string contentType, ContentDisposition contentDisposition)
    {
        var result = new FileContentResultWithContentDisposition(fileContents, contentType, contentDisposition);
        return result;
    }
    
    public ActionResult Report()
    {
        // get a reference to your document or file
        // in this example the report exposes properties for
        // the byte[] data and content-type of the document
        var report = ...
        return File(report.Data, report.ContentType, new ContentDisposition {
            Inline = true,
            FileName = report.FileName
        });
    }
    

    现在文件将使用您选择的文件名和“inline; filename=[filename]”的内容处置标头发送到浏览器。

    希望对你有帮助!

    【讨论】:

    • 我首先采用了 ContentDisposition 辅助类方式,只是为了意识到 MVC 也在内部使用它,但有一些技巧可以正确处理 utf-8 文件名。 ContentDisposition 助手类在必须编码 utf-8 值时做错了。详情请见my comment here
    • ExecuteResult 不可覆盖,怎么办?
    【解决方案5】:

    使用 ASP.NET MVC 将文件流式传输到浏览器的绝对最简单的方法是:

    public ActionResult DownloadFile() {
        return File(@"c:\path\to\somefile.pdf", "application/pdf", "Your Filename.pdf");
    }
    

    这比@azarc3 建议的方法更容易,因为您甚至不需要读取字节。

    归功于:http://prideparrot.com/blog/archive/2012/8/uploading_and_returning_files#how_to_return_a_file_as_response

    ** 编辑 **

    显然我的“答案”与 OP 的问题相同。但我没有面对他遇到的问题。可能这是旧版本的 ASP.NET MVC 的问题?

    【讨论】:

    • 它的问题可以抽象为'MVC 在指定文件名时发送带有attachment 配置的content-disposition 标头,如何让它以inline 发送?测试您的解决方案响应标头,您通常也会看到 attachment
    • 对我来说这就是诀窍。在罗斯迪指出之前,我认为事情没有那么简单
    【解决方案6】:

    我在 ASP.NET Core 中使用 REST API 对其进行了调整。

    public class FileContentWithFileNameResult : FileContentResult
    {
        public FileContentWithFileNameResult(byte[] fileContents, string contentType, string fileName)
       : base(fileContents, contentType)
        {
            FileName = fileName;
        }
    
        public string FileName { get; private set; }
    
        public override Task ExecuteResultAsync(ActionContext context)
        {
            var response = context.HttpContext.Response;  
            response.Headers.Append("Content-Disposition", $"inline; filename={FileName}");
            response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition");
            response.Headers.Append("X-Content-Type-Options", "nosniff");
            return base.ExecuteResultAsync(context);
        }
    }
    

    【讨论】:

      【解决方案7】:
      public FileContentResult GetImage(int productId) { 
           Product prod = repository.Products.FirstOrDefault(p => p.ProductID == productId); 
           if (prod != null) { 
               return File(prod.ImageData, prod.ImageMimeType); 
            } else { 
               return null; 
           } 
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-03-27
        • 1970-01-01
        • 1970-01-01
        • 2020-10-16
        • 2014-12-16
        • 1970-01-01
        • 1970-01-01
        • 2011-11-07
        相关资源
        最近更新 更多