在具有名称的浏览器中使用 ASP.NET MVC FileContentResult 流文件?

发布于 2024-09-08 09:04:33 字数 525 浏览 6 评论 0原文

有没有办法在浏览器中使用特定名称的 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 时,它会显示操作名称作为文件名。

有人遇到过这种情况吗?

Is there a way to stream a file using ASP.NET MVC FileContentResult within the browser with a specific name?

I have noticed that you can either have a FileDialog (Open/Save) or you can stream the file in a browser window, but then it will use the ActionName when you try to save the file.

I have the following scenario:

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

When I use this, I can stream the bytes, but a OPEN/SAVE file dialog is given to the user. I would like to actually stream this file in a browser window.

If I just use the FilePathResult, it shows the file in a browser window, but then when I click on "Save" button to save the file in PDF, it shows me the Action Name as the name of the file.

Has anyone encountered this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

失眠症患者 2024-09-15 09:04:33
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");
}
public ActionResult Index()
{
    byte[] contents = FetchPdfBytes();
    return File(contents, "application/pdf", "test.pdf");
}

and for opening the PDF inside the browser you will need to set the Content-Disposition header:

public ActionResult Index()
{
    byte[] contents = FetchPdfBytes();
    Response.AddHeader("Content-Disposition", "inline; filename=test.pdf");
    return File(contents, "application/pdf");
}
梦一生花开无言 2024-09-15 09:04:33

实际上,最简单的方法是执行以下操作......

byte[] content = your_byte[];

FileContentResult result = new FileContentResult(content, "application/octet-stream") 
{
  FileDownloadName = "your_file_name"
};

return result;

Actually, the absolutely easiest way is to do the following...

byte[] content = your_byte[];

FileContentResult result = new FileContentResult(content, "application/octet-stream") 
{
  FileDownloadName = "your_file_name"
};

return result;
凉城 2024-09-15 09:04:33

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

您可以通过此 URL 进一步阅读:
使用正确的文件名在浏览器中安全下载文件

这给了我一个我的想法是,我刚刚创建了 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" }
            );

这几乎解决了我的问题。

This might be helpful for whoever else faces this problem. I finally figured out a solution. Turns out, even if we use the inline for "content-disposition" and specify a file name, the browsers still do not use the file name. Instead browsers try and interpret the file name based on the Path/URL.

You can read further on this URL:
Securly download file inside browser with correct filename

This gave me an idea, I just created my URL route that would convert the URL and end it with the name of the file I wanted to give the file. So for e.g. my original controller call just consisted of passing the Order Id of the Order being printed. I was expecting the file name to be of the format Order{0}.pdf where {0} is the Order Id. Similarly for quotes, I wanted Quote{0}.pdf.

In my controller, I just went ahead and added an additional parameter to accept the file name. I passed the filename as a parameter in the URL.Action method.

I then created a new route that would map that URL to the format:
http://localhost/ShoppingCart/PrintQuote/1054/Quote1054.pdf


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

This pretty much solved my issue.

贱人配狗天长地久 2024-09-15 09:04:33

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

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

...将导致多个 Content-Disposition 标头发送到浏览器。如果您为其提供文件名,则 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]”。

我希望这有帮助!

Previous answers are correct: adding the line...

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

...will causing multiple Content-Disposition headers to be sent down to the browser. This happens b/c FileContentResult internally applies the header if you supply it with a file name. An alternative, and pretty simple, solution is to simply create a subclass of FileContentResult and override its ExecuteResult() method. Here's an example that instantiates an instance of the System.Net.Mime.ContentDisposition class (the same object used in the internal FileContentResult implementation) and passes it into the new class:

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);
    }
}

In your Controller, or in a base Controller, you can write a simple helper to instantiate a FileContentResultWithContentDisposition and then call it from your action method, like so:

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
    });
}

Now the file will be sent to the browser with the file name you choose and with a content-disposition header of "inline; filename=[filename]".

I hope that helps!

没企图 2024-09-15 09:04:33

使用 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 的问题?

The absolute easiest way to stream a file into browser using ASP.NET MVC is this:

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

This is easier than the method suggested by @azarc3 since you don't even need to read the bytes.

Credit goes to: http://prideparrot.com/blog/archive/2012/8/uploading_and_returning_files#how_to_return_a_file_as_response

** Edit **

Apparently my 'answer' is the same as the OP's question. But I am not facing the problem he is having. Probably this was an issue with older version of ASP.NET MVC?

时光礼记 2024-09-15 09:04:33

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

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);
    }
}

I adapted it in ASP.NET Core with 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", 
quot;inline; filename={FileName}");
        response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition");
        response.Headers.Append("X-Content-Type-Options", "nosniff");
        return base.ExecuteResultAsync(context);
    }
}
请别遗忘我 2024-09-15 09:04:33
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; 
     } 
}
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; 
     } 
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文