Thierry Brémard
Thierry Brémard

Reputation: 899

Blazor Server: Force file download

In the context of the development of my website MySpector.com developed with Blazor Server, I want to give the user the possibility to download files which are stored on the disk of the machine. To do this I want to create a Blazor Component / Page ( I do not know what) which will:

I am having problem to understand how I can program the http response headers and content with Blazor. I have seen a potential solution on Blazor download on microsoft but I do not like the idea to create a RAZOR page inside a BLAZOR solution, as this introduce a component organized in a different way than the other ( 2 files: .cshtml + .cshtml.cs, instead of 1 .razor file)

see below Downloader.cshtml in the file hierarchy which looks like different than the other pages. Downloader.cshtml

I am also aware of the 'different layers' of blazor server where a page is only a subpart of a bigger html context so I see the problem, but I woudl like to find a easy way only to override http GET via a .razor single file... if this is possible.

Regards.

Upvotes: 1

Views: 1251

Answers (1)

Thierry Brémard
Thierry Brémard

Reputation: 899

You can get control of the http response with headers by using a Asp net core Controller. This is compatible with Blazor server technology. To do so, you can folow up the 2 links below:

I added a controller directory with the downloader controller:

downloader controller

I patch the code of startup with this diff: Diff of startup.cs

Then I add the code for the downloader controller:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Net.Mime;
using System.Web;
using NLog;
using MySpector.Core;
using MySpector.Objects;

//https://chanmingman.wordpress.com/2020/07/12/add-web-api-controller-to-blazor-project-asp-net/
//https://stackoverflow.com/questions/5826649/returning-a-file-to-view-download-in-asp-net-mvc


namespace FrontEnd.Pages
{
    [Route("api/[controller]")]
    [ApiController]
    public class DownloadController : Controller
    {
        private static Logger _log = LogManager.GetCurrentClassLogger();
        private ResultStorage _resultStorage;
    // GET /api/Download/3
    [HttpGet("{resultDbId:int}")] 
    public ActionResult Get(int resultDbId)
    {
        bool isConnected = ServiceLocator.Instance.Repo.Connect();
        if (!isConnected)
        {
            _log.Error("Cannot connect");
            _log.Error("Returning Http 404");
            return NotFound();
        }
        else
        {
            _log.Debug("Connected to db");
            _log.Debug("Input: ResultDbId=" + resultDbId);
            _resultStorage = ServiceLocator.Instance.Repo.GetSingleResult(resultDbId);
        }
        if(_resultStorage==null)
        {
            _log.Error("Returning Http 404");
            return NotFound();
        }
        string filename = _resultStorage.File.FilePath;
        var cd = new ContentDisposition
        {
            FileName = filename,
            Inline = true,
        };
        Response.Headers.Add("Content-Disposition", cd.ToString());
        byte[] filedata = System.IO.File.ReadAllBytes(filename);
        string contentType = "application/octet-stream";
        return File(filedata, contentType);
    }
}
}

You can then call this module with:

        string downloadUri = "api/Download/" + @_report.Result.DbId;
        <NavLink href="@downloadUri">Download raw file</NavLink><br />

Upvotes: 0

Related Questions