Search code examples
c#asp.net-core-mvcasp.net-core-7.0html-target

ASP.NET Core 7/Razor: how do I direct controller output to a new browser tab?


I have a Razor page with a list of "attachments" I'd like users to be able to "preview". Each "preview" link calls a controller action, which does a database looking, and returns the attachment as an ASP.NET FileStreamResult.

Problem:

When I click on the link, it "downloads" the file. Instead, I need it to display to a new browser tab.

Razor.cshtml:

<a href="@Model.ParseUrl(Model.MSServiceArea.QE042)" target="_blank" >@Model.ParseFileName(Model.MSServiceArea.QE042)</a>&nbsp;

Dynamically generated href:

https://localhost:44342/LifeCycle/Attachments/Download?ModuleName=MSServiceArea&LookupKey=abc

Controller:

public IActionResult OnGet(string ModuleName, string LookupKey)
{
    string fileName, mimeType;
    Stream stream = Attachment.Download(ctx, null, ModuleName, LookupKey, out fileName, out mimeType);
    stream.Position = 0;

    var fileStreamResult = new FileStreamResult(stream, mimeType)
    {
        FileDownloadName = fileName
    };

    return fileStreamResult;
}

Goal::

I'd like to be able to:

  • Have the user click on a link in a Razor page, then
  • Read an "attachment" (of some arbitrary MIME type) from a database, and finally
  • Display it to the user in a new browser tab

Q: I THOUGHT adding target="_blank" to my anchor element would be sufficient. What am I missing?


Update

I got PARTIAL success by returning HTML (which now successfully displays in a new browser tab, instead of "downloading" the file). The HTML wraps an element, whose "src" is the original controller, renamed "OnGetDownload()". OnGetDownload() does the DB lookup and returns the binary "attachment" data.

Current problem:

It works fine if the "attachment" happens to be an image file... but DOESN'T work correctly for other file types (e.g. HTML)>

"Old" controller (simply returns the binary attachment data):

public IActionResult OnGetDownload(string ModuleName, string LookupKey)
{
    string fileName, mimeType;
    Stream stream = Attachment.Download(ctx, null, ModuleName, LookupKey, out fileName, out mimeType);
    stream.Position = 0;
    var fileStreamResult = new FileStreamResult(stream, mimeType)
    {
        FileDownloadName = fileName
    };
    return fileStreamResult;
}

New "Preview" controller (wraps the attachment in static HTML):

public IActionResult OnGetPreview(string ModuleName, string LookupKey)
{
    string html = "<html><body><img src=\"URL\" ></body></html>";
    string url = "./Download/" + HttpContext.Request.QueryString;
    html = html.Replace("URL", url);
    return base.Content(html, "text/html");
}

Results:

  • .png, .gif, etc.: <a ... target="_blank"> successfully displays the requested attachment in a new browser tab.
  • *.pdf, *.html, etc: brings up a new browser tab, but displays "broken image" icon instead of the attachment.

Solution

  • You can't always control what the browser is doing here; at the end of the day, you're starting to get towards territory that is up to the user and their preferences.

    However, you can give the browser hints about what you want to do, and in most cases it will follow them (unless the user has indicated otherwise).

    Currently, you're telling the browser to download the file by setting the FileDownloadName property on FileStreamResult. Removing this should give you the results you're after (no need for the separate preview controller).

    If you want to try to force the browser to preview it despite user settings, you could use your preview method, but use an iframe instead of an image. As far as I know, there's no absolute guarantee that the browser won't still download the file out of the iframe, but in 99% of cases it should just display.

    So, here's a summary of what you need:

    • <a target="_blank" href="/path/to/handler">
    • Your modified handler:
    public IActionResult OnGetDownload(string ModuleName, string LookupKey)
    {
        string fileName, mimeType;
        Stream stream = Attachment.Download(ctx, null, ModuleName, LookupKey, out fileName, out mimeType);
        stream.Position = 0;
        var fileStreamResult = new FileStreamResult(stream, mimeType);
        // Notice no initialiser for FileDownloadName
    
        return fileStreamResult;
    }
    
    • Remove OnGetPreview